Migrate audit reports to the files table
Signed-off-by: Ludovic Vielle <ludovic@probo.com>
This commit is contained in:
@@ -1456,9 +1456,9 @@ func TestAudit_UploadReport(t *testing.T) {
|
|||||||
uploadAuditReport(input: $input) {
|
uploadAuditReport(input: $input) {
|
||||||
audit {
|
audit {
|
||||||
id
|
id
|
||||||
report {
|
reportFile {
|
||||||
id
|
id
|
||||||
filename
|
fileName
|
||||||
size
|
size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1473,11 +1473,11 @@ func TestAudit_UploadReport(t *testing.T) {
|
|||||||
UploadAuditReport struct {
|
UploadAuditReport struct {
|
||||||
Audit struct {
|
Audit struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Report *struct {
|
ReportFile *struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Filename string `json:"filename"`
|
FileName string `json:"fileName"`
|
||||||
Size int `json:"size"`
|
Size int64 `json:"size"`
|
||||||
} `json:"report"`
|
} `json:"reportFile"`
|
||||||
} `json:"audit"`
|
} `json:"audit"`
|
||||||
} `json:"uploadAuditReport"`
|
} `json:"uploadAuditReport"`
|
||||||
}
|
}
|
||||||
@@ -1495,9 +1495,9 @@ func TestAudit_UploadReport(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, auditID, result.UploadAuditReport.Audit.ID)
|
assert.Equal(t, auditID, result.UploadAuditReport.Audit.ID)
|
||||||
require.NotNil(t, result.UploadAuditReport.Audit.Report)
|
require.NotNil(t, result.UploadAuditReport.Audit.ReportFile)
|
||||||
assert.Equal(t, "audit-report.pdf", result.UploadAuditReport.Audit.Report.Filename)
|
assert.Equal(t, "audit-report.pdf", result.UploadAuditReport.Audit.ReportFile.FileName)
|
||||||
assert.Equal(t, len(pdfContent), result.UploadAuditReport.Audit.Report.Size)
|
assert.Equal(t, int64(len(pdfContent)), result.UploadAuditReport.Audit.ReportFile.Size)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("upload replaces existing report", func(t *testing.T) {
|
t.Run("upload replaces existing report", func(t *testing.T) {
|
||||||
@@ -1508,9 +1508,9 @@ func TestAudit_UploadReport(t *testing.T) {
|
|||||||
uploadAuditReport(input: $input) {
|
uploadAuditReport(input: $input) {
|
||||||
audit {
|
audit {
|
||||||
id
|
id
|
||||||
report {
|
reportFile {
|
||||||
id
|
id
|
||||||
filename
|
fileName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1524,10 +1524,10 @@ func TestAudit_UploadReport(t *testing.T) {
|
|||||||
UploadAuditReport struct {
|
UploadAuditReport struct {
|
||||||
Audit struct {
|
Audit struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Report *struct {
|
ReportFile *struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Filename string `json:"filename"`
|
FileName string `json:"fileName"`
|
||||||
} `json:"report"`
|
} `json:"reportFile"`
|
||||||
} `json:"audit"`
|
} `json:"audit"`
|
||||||
} `json:"uploadAuditReport"`
|
} `json:"uploadAuditReport"`
|
||||||
}
|
}
|
||||||
@@ -1544,7 +1544,7 @@ func TestAudit_UploadReport(t *testing.T) {
|
|||||||
}, &result1)
|
}, &result1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
firstReportID := result1.UploadAuditReport.Audit.Report.ID
|
firstReportID := result1.UploadAuditReport.Audit.ReportFile.ID
|
||||||
|
|
||||||
// Upload second report (should replace)
|
// Upload second report (should replace)
|
||||||
pdfContent2 := []byte("%PDF-1.4\n1 0 obj\n<< /Type /Catalog /Version /1.4 >>\nendobj\ntrailer\n<< /Root 1 0 R >>\n%%EOF")
|
pdfContent2 := []byte("%PDF-1.4\n1 0 obj\n<< /Type /Catalog /Version /1.4 >>\nendobj\ntrailer\n<< /Root 1 0 R >>\n%%EOF")
|
||||||
@@ -1553,10 +1553,10 @@ func TestAudit_UploadReport(t *testing.T) {
|
|||||||
UploadAuditReport struct {
|
UploadAuditReport struct {
|
||||||
Audit struct {
|
Audit struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Report *struct {
|
ReportFile *struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Filename string `json:"filename"`
|
FileName string `json:"fileName"`
|
||||||
} `json:"report"`
|
} `json:"reportFile"`
|
||||||
} `json:"audit"`
|
} `json:"audit"`
|
||||||
} `json:"uploadAuditReport"`
|
} `json:"uploadAuditReport"`
|
||||||
}
|
}
|
||||||
@@ -1573,8 +1573,8 @@ func TestAudit_UploadReport(t *testing.T) {
|
|||||||
}, &result2)
|
}, &result2)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, "second-report.pdf", result2.UploadAuditReport.Audit.Report.Filename)
|
assert.Equal(t, "second-report.pdf", result2.UploadAuditReport.Audit.ReportFile.FileName)
|
||||||
assert.NotEqual(t, firstReportID, result2.UploadAuditReport.Audit.Report.ID, "Report ID should change when replaced")
|
assert.NotEqual(t, firstReportID, result2.UploadAuditReport.Audit.ReportFile.ID, "Report file ID should change when replaced")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1782,7 +1782,7 @@ func TestAudit_DeleteReport(t *testing.T) {
|
|||||||
uploadAuditReport(input: $input) {
|
uploadAuditReport(input: $input) {
|
||||||
audit {
|
audit {
|
||||||
id
|
id
|
||||||
report {
|
reportFile {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1810,7 +1810,7 @@ func TestAudit_DeleteReport(t *testing.T) {
|
|||||||
deleteAuditReport(input: $input) {
|
deleteAuditReport(input: $input) {
|
||||||
audit {
|
audit {
|
||||||
id
|
id
|
||||||
report {
|
reportFile {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1822,9 +1822,9 @@ func TestAudit_DeleteReport(t *testing.T) {
|
|||||||
DeleteAuditReport struct {
|
DeleteAuditReport struct {
|
||||||
Audit struct {
|
Audit struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Report *struct {
|
ReportFile *struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
} `json:"report"`
|
} `json:"reportFile"`
|
||||||
} `json:"audit"`
|
} `json:"audit"`
|
||||||
} `json:"deleteAuditReport"`
|
} `json:"deleteAuditReport"`
|
||||||
}
|
}
|
||||||
@@ -1836,7 +1836,7 @@ func TestAudit_DeleteReport(t *testing.T) {
|
|||||||
}, &deleteResult)
|
}, &deleteResult)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, auditID, deleteResult.DeleteAuditReport.Audit.ID)
|
assert.Equal(t, auditID, deleteResult.DeleteAuditReport.Audit.ID)
|
||||||
assert.Nil(t, deleteResult.DeleteAuditReport.Audit.Report, "Report should be nil after deletion")
|
assert.Nil(t, deleteResult.DeleteAuditReport.Audit.ReportFile, "Report file should be nil after deletion")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ type (
|
|||||||
Name *string `db:"name"`
|
Name *string `db:"name"`
|
||||||
OrganizationID gid.GID `db:"organization_id"`
|
OrganizationID gid.GID `db:"organization_id"`
|
||||||
FrameworkID gid.GID `db:"framework_id"`
|
FrameworkID gid.GID `db:"framework_id"`
|
||||||
ReportID *gid.GID `db:"report_id"`
|
ReportFileID *gid.GID `db:"report_file_id"`
|
||||||
ValidFrom *time.Time `db:"valid_from"`
|
ValidFrom *time.Time `db:"valid_from"`
|
||||||
ValidUntil *time.Time `db:"valid_until"`
|
ValidUntil *time.Time `db:"valid_until"`
|
||||||
State AuditState `db:"state"`
|
State AuditState `db:"state"`
|
||||||
@@ -113,7 +113,7 @@ SELECT
|
|||||||
name,
|
name,
|
||||||
organization_id,
|
organization_id,
|
||||||
framework_id,
|
framework_id,
|
||||||
report_id,
|
report_file_id,
|
||||||
valid_from,
|
valid_from,
|
||||||
valid_until,
|
valid_until,
|
||||||
state,
|
state,
|
||||||
@@ -199,7 +199,7 @@ SELECT
|
|||||||
name,
|
name,
|
||||||
organization_id,
|
organization_id,
|
||||||
framework_id,
|
framework_id,
|
||||||
report_id,
|
report_file_id,
|
||||||
valid_from,
|
valid_from,
|
||||||
valid_until,
|
valid_until,
|
||||||
state,
|
state,
|
||||||
@@ -250,7 +250,7 @@ SELECT
|
|||||||
name,
|
name,
|
||||||
organization_id,
|
organization_id,
|
||||||
framework_id,
|
framework_id,
|
||||||
report_id,
|
report_file_id,
|
||||||
valid_from,
|
valid_from,
|
||||||
valid_until,
|
valid_until,
|
||||||
state,
|
state,
|
||||||
@@ -299,7 +299,7 @@ INSERT INTO audits (
|
|||||||
tenant_id,
|
tenant_id,
|
||||||
organization_id,
|
organization_id,
|
||||||
framework_id,
|
framework_id,
|
||||||
report_id,
|
report_file_id,
|
||||||
valid_from,
|
valid_from,
|
||||||
valid_until,
|
valid_until,
|
||||||
state,
|
state,
|
||||||
@@ -312,7 +312,7 @@ INSERT INTO audits (
|
|||||||
@tenant_id,
|
@tenant_id,
|
||||||
@organization_id,
|
@organization_id,
|
||||||
@framework_id,
|
@framework_id,
|
||||||
@report_id,
|
@report_file_id,
|
||||||
@valid_from,
|
@valid_from,
|
||||||
@valid_until,
|
@valid_until,
|
||||||
@state,
|
@state,
|
||||||
@@ -328,7 +328,7 @@ INSERT INTO audits (
|
|||||||
"tenant_id": scope.GetTenantID(),
|
"tenant_id": scope.GetTenantID(),
|
||||||
"organization_id": a.OrganizationID,
|
"organization_id": a.OrganizationID,
|
||||||
"framework_id": a.FrameworkID,
|
"framework_id": a.FrameworkID,
|
||||||
"report_id": a.ReportID,
|
"report_file_id": a.ReportFileID,
|
||||||
"valid_from": a.ValidFrom,
|
"valid_from": a.ValidFrom,
|
||||||
"valid_until": a.ValidUntil,
|
"valid_until": a.ValidUntil,
|
||||||
"state": a.State,
|
"state": a.State,
|
||||||
@@ -354,7 +354,7 @@ func (a *Audit) Update(
|
|||||||
UPDATE audits
|
UPDATE audits
|
||||||
SET
|
SET
|
||||||
name = @name,
|
name = @name,
|
||||||
report_id = @report_id,
|
report_file_id = @report_file_id,
|
||||||
valid_from = @valid_from,
|
valid_from = @valid_from,
|
||||||
valid_until = @valid_until,
|
valid_until = @valid_until,
|
||||||
state = @state,
|
state = @state,
|
||||||
@@ -370,7 +370,7 @@ WHERE
|
|||||||
args := pgx.StrictNamedArgs{
|
args := pgx.StrictNamedArgs{
|
||||||
"id": a.ID,
|
"id": a.ID,
|
||||||
"name": a.Name,
|
"name": a.Name,
|
||||||
"report_id": a.ReportID,
|
"report_file_id": a.ReportFileID,
|
||||||
"valid_from": a.ValidFrom,
|
"valid_from": a.ValidFrom,
|
||||||
"valid_until": a.ValidUntil,
|
"valid_until": a.ValidUntil,
|
||||||
"state": a.State,
|
"state": a.State,
|
||||||
@@ -427,7 +427,7 @@ WITH audits_by_control AS (
|
|||||||
a.name,
|
a.name,
|
||||||
a.organization_id,
|
a.organization_id,
|
||||||
a.framework_id,
|
a.framework_id,
|
||||||
a.report_id,
|
a.report_file_id,
|
||||||
a.valid_from,
|
a.valid_from,
|
||||||
a.valid_until,
|
a.valid_until,
|
||||||
a.state,
|
a.state,
|
||||||
@@ -446,7 +446,7 @@ SELECT
|
|||||||
name,
|
name,
|
||||||
organization_id,
|
organization_id,
|
||||||
framework_id,
|
framework_id,
|
||||||
report_id,
|
report_file_id,
|
||||||
valid_from,
|
valid_from,
|
||||||
valid_until,
|
valid_until,
|
||||||
state,
|
state,
|
||||||
@@ -494,7 +494,7 @@ WITH audits_by_finding AS (
|
|||||||
a.name,
|
a.name,
|
||||||
a.organization_id,
|
a.organization_id,
|
||||||
a.framework_id,
|
a.framework_id,
|
||||||
a.report_id,
|
a.report_file_id,
|
||||||
a.valid_from,
|
a.valid_from,
|
||||||
a.valid_until,
|
a.valid_until,
|
||||||
a.state,
|
a.state,
|
||||||
@@ -513,7 +513,7 @@ SELECT
|
|||||||
name,
|
name,
|
||||||
organization_id,
|
organization_id,
|
||||||
framework_id,
|
framework_id,
|
||||||
report_id,
|
report_file_id,
|
||||||
valid_from,
|
valid_from,
|
||||||
valid_until,
|
valid_until,
|
||||||
state,
|
state,
|
||||||
@@ -632,11 +632,11 @@ WHERE
|
|||||||
return count, nil
|
return count, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Audit) LoadByReportID(
|
func (a *Audit) LoadByReportFileID(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
conn pg.Querier,
|
conn pg.Querier,
|
||||||
scope Scoper,
|
scope Scoper,
|
||||||
reportID gid.GID,
|
fileID gid.GID,
|
||||||
) error {
|
) error {
|
||||||
q := `
|
q := `
|
||||||
SELECT
|
SELECT
|
||||||
@@ -644,7 +644,7 @@ SELECT
|
|||||||
name,
|
name,
|
||||||
organization_id,
|
organization_id,
|
||||||
framework_id,
|
framework_id,
|
||||||
report_id,
|
report_file_id,
|
||||||
valid_from,
|
valid_from,
|
||||||
valid_until,
|
valid_until,
|
||||||
state,
|
state,
|
||||||
@@ -654,12 +654,12 @@ SELECT
|
|||||||
FROM
|
FROM
|
||||||
audits
|
audits
|
||||||
WHERE %s
|
WHERE %s
|
||||||
AND report_id = @report_id
|
AND report_file_id = @report_file_id
|
||||||
LIMIT 1;
|
LIMIT 1;
|
||||||
`
|
`
|
||||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||||
|
|
||||||
args := pgx.StrictNamedArgs{"report_id": reportID}
|
args := pgx.StrictNamedArgs{"report_file_id": fileID}
|
||||||
maps.Copy(args, scope.SQLArguments())
|
maps.Copy(args, scope.SQLArguments())
|
||||||
|
|
||||||
rows, err := conn.Query(ctx, q, args)
|
rows, err := conn.Query(ctx, q, args)
|
||||||
@@ -681,6 +681,52 @@ LIMIT 1;
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (as *Audits) LoadByReportFileIDs(
|
||||||
|
ctx context.Context,
|
||||||
|
conn pg.Querier,
|
||||||
|
scope Scoper,
|
||||||
|
fileIDs []gid.GID,
|
||||||
|
) error {
|
||||||
|
q := `
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
organization_id,
|
||||||
|
framework_id,
|
||||||
|
report_file_id,
|
||||||
|
valid_from,
|
||||||
|
valid_until,
|
||||||
|
state,
|
||||||
|
trust_center_visibility,
|
||||||
|
created_at,
|
||||||
|
updated_at
|
||||||
|
FROM
|
||||||
|
audits
|
||||||
|
WHERE
|
||||||
|
%s
|
||||||
|
AND report_file_id = ANY(@file_ids)
|
||||||
|
`
|
||||||
|
|
||||||
|
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||||
|
|
||||||
|
args := pgx.StrictNamedArgs{"file_ids": fileIDs}
|
||||||
|
maps.Copy(args, scope.SQLArguments())
|
||||||
|
|
||||||
|
rows, err := conn.Query(ctx, q, args)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot query audits by report file IDs: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
audits, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[Audit])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot collect audits by report file IDs: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
*as = audits
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (as *Audits) LoadByReportIDs(
|
func (as *Audits) LoadByReportIDs(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
conn pg.Querier,
|
conn pg.Querier,
|
||||||
|
|||||||
@@ -53,8 +53,6 @@ func ResourceTypeName(entityType uint16) string {
|
|||||||
return "Datum"
|
return "Datum"
|
||||||
case AuditEntityType:
|
case AuditEntityType:
|
||||||
return "Audit"
|
return "Audit"
|
||||||
case ReportEntityType:
|
|
||||||
return "Report"
|
|
||||||
case TrustCenterEntityType:
|
case TrustCenterEntityType:
|
||||||
return "TrustCenter"
|
return "TrustCenter"
|
||||||
case TrustCenterAccessEntityType:
|
case TrustCenterAccessEntityType:
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ const (
|
|||||||
AssetEntityType uint16 = 18
|
AssetEntityType uint16 = 18
|
||||||
DatumEntityType uint16 = 19
|
DatumEntityType uint16 = 19
|
||||||
AuditEntityType uint16 = 20
|
AuditEntityType uint16 = 20
|
||||||
ReportEntityType uint16 = 21
|
_ uint16 = 21 // ReportEntityType - removed
|
||||||
TrustCenterEntityType uint16 = 22
|
TrustCenterEntityType uint16 = 22
|
||||||
TrustCenterAccessEntityType uint16 = 23
|
TrustCenterAccessEntityType uint16 = 23
|
||||||
ThirdPartyBusinessAssociateAgreementEntityType uint16 = 24
|
ThirdPartyBusinessAssociateAgreementEntityType uint16 = 24
|
||||||
@@ -168,8 +168,6 @@ func NewEntityFromID(id gid.GID) (any, bool) {
|
|||||||
return &Datum{ID: id}, true
|
return &Datum{ID: id}, true
|
||||||
case AuditEntityType:
|
case AuditEntityType:
|
||||||
return &Audit{ID: id}, true
|
return &Audit{ID: id}, true
|
||||||
case ReportEntityType:
|
|
||||||
return &Report{ID: id}, true
|
|
||||||
case TrustCenterEntityType:
|
case TrustCenterEntityType:
|
||||||
return &TrustCenter{ID: id}, true
|
return &TrustCenter{ID: id}, true
|
||||||
case TrustCenterAccessEntityType:
|
case TrustCenterAccessEntityType:
|
||||||
|
|||||||
@@ -157,6 +157,10 @@ LIMIT 1;
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadByIDs Loads every given files, whether they are active or not. See
|
||||||
|
// Files.LoadActiveByIDs for a safer option.
|
||||||
|
//
|
||||||
|
// DISCLAIMER: use with caution on user-facing features.
|
||||||
func (f *Files) LoadByIDs(
|
func (f *Files) LoadByIDs(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
conn pg.Querier,
|
conn pg.Querier,
|
||||||
@@ -269,6 +273,59 @@ VALUES (
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *File) LoadActiveByID(
|
||||||
|
ctx context.Context,
|
||||||
|
conn pg.Querier,
|
||||||
|
scope Scoper,
|
||||||
|
fileID gid.GID,
|
||||||
|
) error {
|
||||||
|
q := `
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
organization_id,
|
||||||
|
bucket_name,
|
||||||
|
mime_type,
|
||||||
|
file_name,
|
||||||
|
file_key,
|
||||||
|
file_size,
|
||||||
|
visibility,
|
||||||
|
created_at,
|
||||||
|
updated_at,
|
||||||
|
deleted_at
|
||||||
|
FROM
|
||||||
|
files
|
||||||
|
WHERE
|
||||||
|
%s
|
||||||
|
AND id = @file_id
|
||||||
|
AND deleted_at IS NULL
|
||||||
|
LIMIT 1;
|
||||||
|
`
|
||||||
|
|
||||||
|
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||||
|
|
||||||
|
args := pgx.StrictNamedArgs{"file_id": fileID}
|
||||||
|
maps.Copy(args, scope.SQLArguments())
|
||||||
|
|
||||||
|
rows, err := conn.Query(ctx, q, args)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot query file: %w", err)
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
file, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[File])
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, pgx.ErrNoRows) {
|
||||||
|
return ErrResourceNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("cannot collect file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
*f = file
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (f *File) LoadPublicByID(
|
func (f *File) LoadPublicByID(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
conn pg.Querier,
|
conn pg.Querier,
|
||||||
|
|||||||
@@ -1,295 +0,0 @@
|
|||||||
// Copyright (c) 2025-2026 Probo Inc <hello@getprobo.com>.
|
|
||||||
//
|
|
||||||
// Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
// purpose with or without fee is hereby granted, provided that the above
|
|
||||||
// copyright notice and this permission notice appear in all copies.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
||||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
||||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
||||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
||||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
||||||
// PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
|
|
||||||
package coredata
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"maps"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/jackc/pgx/v5"
|
|
||||||
"go.gearno.de/kit/pg"
|
|
||||||
"go.probo.inc/probo/pkg/gid"
|
|
||||||
"go.probo.inc/probo/pkg/iam/policy"
|
|
||||||
"go.probo.inc/probo/pkg/page"
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
|
||||||
Report struct {
|
|
||||||
ID gid.GID `db:"id"`
|
|
||||||
OrganizationID gid.GID `db:"organization_id"`
|
|
||||||
ObjectKey string `db:"object_key"`
|
|
||||||
MimeType string `db:"mime_type"`
|
|
||||||
Filename string `db:"filename"`
|
|
||||||
Size int64 `db:"size"`
|
|
||||||
CreatedAt time.Time `db:"created_at"`
|
|
||||||
UpdatedAt time.Time `db:"updated_at"`
|
|
||||||
}
|
|
||||||
|
|
||||||
Reports []*Report
|
|
||||||
)
|
|
||||||
|
|
||||||
func (r *Report) AuthorizationAttributes(
|
|
||||||
ctx context.Context,
|
|
||||||
conn pg.Querier,
|
|
||||||
resourceIDs []gid.GID,
|
|
||||||
) (policy.AttributesByID, error) {
|
|
||||||
q := `SELECT id, organization_id FROM reports WHERE id = ANY(@resource_ids::text[])`
|
|
||||||
|
|
||||||
args := pgx.StrictNamedArgs{
|
|
||||||
"resource_ids": resourceIDs,
|
|
||||||
}
|
|
||||||
|
|
||||||
rows, err := conn.Query(ctx, q, args)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("cannot query authorization attributes: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
attrsByID := make(policy.AttributesByID)
|
|
||||||
|
|
||||||
for rows.Next() {
|
|
||||||
var id, organizationID gid.GID
|
|
||||||
|
|
||||||
if err := rows.Scan(&id, &organizationID); err != nil {
|
|
||||||
return nil, fmt.Errorf("cannot scan authorization attributes: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
attrsByID[id] = policy.Attributes{
|
|
||||||
"organization_id": organizationID.String(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := rows.Err(); err != nil {
|
|
||||||
return nil, fmt.Errorf("cannot iterate authorization attributes: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return attrsByID, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Report) LoadByID(
|
|
||||||
ctx context.Context,
|
|
||||||
conn pg.Querier,
|
|
||||||
scope Scoper,
|
|
||||||
reportID gid.GID,
|
|
||||||
) error {
|
|
||||||
q := `
|
|
||||||
SELECT
|
|
||||||
id,
|
|
||||||
organization_id,
|
|
||||||
object_key,
|
|
||||||
mime_type,
|
|
||||||
filename,
|
|
||||||
size,
|
|
||||||
created_at,
|
|
||||||
updated_at
|
|
||||||
FROM
|
|
||||||
reports
|
|
||||||
WHERE
|
|
||||||
%s
|
|
||||||
AND id = @report_id
|
|
||||||
LIMIT 1;
|
|
||||||
`
|
|
||||||
|
|
||||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
|
||||||
|
|
||||||
args := pgx.StrictNamedArgs{"report_id": reportID}
|
|
||||||
maps.Copy(args, scope.SQLArguments())
|
|
||||||
|
|
||||||
rows, err := conn.Query(ctx, q, args)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("cannot query report: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
report, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[Report])
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(err, pgx.ErrNoRows) {
|
|
||||||
return ErrResourceNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Errorf("cannot collect report: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
*r = report
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Reports) LoadByIDs(
|
|
||||||
ctx context.Context,
|
|
||||||
conn pg.Querier,
|
|
||||||
scope Scoper,
|
|
||||||
reportIDs []gid.GID,
|
|
||||||
) error {
|
|
||||||
q := `
|
|
||||||
SELECT
|
|
||||||
id,
|
|
||||||
organization_id,
|
|
||||||
object_key,
|
|
||||||
mime_type,
|
|
||||||
filename,
|
|
||||||
size,
|
|
||||||
created_at,
|
|
||||||
updated_at
|
|
||||||
FROM
|
|
||||||
reports
|
|
||||||
WHERE
|
|
||||||
%s
|
|
||||||
AND id = ANY(@report_ids);
|
|
||||||
`
|
|
||||||
|
|
||||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
|
||||||
|
|
||||||
args := pgx.StrictNamedArgs{"report_ids": reportIDs}
|
|
||||||
maps.Copy(args, scope.SQLArguments())
|
|
||||||
|
|
||||||
rows, err := conn.Query(ctx, q, args)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("cannot query report: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
reports, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[Report])
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("cannot collect reports: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
*r = reports
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Report) Insert(
|
|
||||||
ctx context.Context,
|
|
||||||
conn pg.Tx,
|
|
||||||
scope Scoper,
|
|
||||||
) error {
|
|
||||||
q := `
|
|
||||||
INSERT INTO reports (
|
|
||||||
id,
|
|
||||||
tenant_id,
|
|
||||||
organization_id,
|
|
||||||
object_key,
|
|
||||||
mime_type,
|
|
||||||
filename,
|
|
||||||
size,
|
|
||||||
created_at,
|
|
||||||
updated_at
|
|
||||||
) VALUES (
|
|
||||||
@id,
|
|
||||||
@tenant_id,
|
|
||||||
@organization_id,
|
|
||||||
@object_key,
|
|
||||||
@mime_type,
|
|
||||||
@filename,
|
|
||||||
@size,
|
|
||||||
@created_at,
|
|
||||||
@updated_at
|
|
||||||
)
|
|
||||||
`
|
|
||||||
|
|
||||||
args := pgx.StrictNamedArgs{
|
|
||||||
"id": r.ID,
|
|
||||||
"tenant_id": scope.GetTenantID(),
|
|
||||||
"organization_id": r.OrganizationID,
|
|
||||||
"object_key": r.ObjectKey,
|
|
||||||
"mime_type": r.MimeType,
|
|
||||||
"filename": r.Filename,
|
|
||||||
"size": r.Size,
|
|
||||||
"created_at": r.CreatedAt,
|
|
||||||
"updated_at": r.UpdatedAt,
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := conn.Exec(ctx, q, args)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("cannot insert report: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Report) Update(
|
|
||||||
ctx context.Context,
|
|
||||||
conn pg.Tx,
|
|
||||||
scope Scoper,
|
|
||||||
) error {
|
|
||||||
q := `
|
|
||||||
UPDATE reports
|
|
||||||
SET
|
|
||||||
object_key = @object_key,
|
|
||||||
mime_type = @mime_type,
|
|
||||||
filename = @filename,
|
|
||||||
size = @size,
|
|
||||||
updated_at = @updated_at
|
|
||||||
WHERE
|
|
||||||
%s
|
|
||||||
AND id = @id
|
|
||||||
`
|
|
||||||
|
|
||||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
|
||||||
|
|
||||||
args := pgx.StrictNamedArgs{
|
|
||||||
"id": r.ID,
|
|
||||||
"object_key": r.ObjectKey,
|
|
||||||
"mime_type": r.MimeType,
|
|
||||||
"filename": r.Filename,
|
|
||||||
"size": r.Size,
|
|
||||||
"updated_at": r.UpdatedAt,
|
|
||||||
}
|
|
||||||
maps.Copy(args, scope.SQLArguments())
|
|
||||||
|
|
||||||
_, err := conn.Exec(ctx, q, args)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("cannot update report: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Report) Delete(
|
|
||||||
ctx context.Context,
|
|
||||||
conn pg.Tx,
|
|
||||||
scope Scoper,
|
|
||||||
) error {
|
|
||||||
q := `
|
|
||||||
DELETE FROM reports
|
|
||||||
WHERE
|
|
||||||
%s
|
|
||||||
AND id = @id
|
|
||||||
`
|
|
||||||
|
|
||||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
|
||||||
|
|
||||||
args := pgx.StrictNamedArgs{"id": r.ID}
|
|
||||||
maps.Copy(args, scope.SQLArguments())
|
|
||||||
|
|
||||||
_, err := conn.Exec(ctx, q, args)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("cannot delete report: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Report) CursorKey(orderBy ReportOrderField) page.CursorKey {
|
|
||||||
switch orderBy {
|
|
||||||
case ReportOrderFieldID:
|
|
||||||
return page.NewCursorKey(r.ID, r.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
panic(fmt.Sprintf("unsupported order by: %s", orderBy))
|
|
||||||
}
|
|
||||||
@@ -35,7 +35,7 @@ type (
|
|||||||
OrganizationID gid.GID `db:"organization_id"`
|
OrganizationID gid.GID `db:"organization_id"`
|
||||||
TrustCenterAccessID gid.GID `db:"trust_center_access_id"`
|
TrustCenterAccessID gid.GID `db:"trust_center_access_id"`
|
||||||
DocumentID *gid.GID `db:"document_id"`
|
DocumentID *gid.GID `db:"document_id"`
|
||||||
ReportID *gid.GID `db:"report_id"`
|
ReportFileID *gid.GID `db:"report_file_id"`
|
||||||
TrustCenterFileID *gid.GID `db:"trust_center_file_id"`
|
TrustCenterFileID *gid.GID `db:"trust_center_file_id"`
|
||||||
Status TrustCenterDocumentAccessStatus `db:"status"`
|
Status TrustCenterDocumentAccessStatus `db:"status"`
|
||||||
CreatedAt time.Time `db:"created_at"`
|
CreatedAt time.Time `db:"created_at"`
|
||||||
@@ -105,7 +105,7 @@ SELECT
|
|||||||
organization_id,
|
organization_id,
|
||||||
trust_center_access_id,
|
trust_center_access_id,
|
||||||
document_id,
|
document_id,
|
||||||
report_id,
|
report_file_id,
|
||||||
trust_center_file_id,
|
trust_center_file_id,
|
||||||
status,
|
status,
|
||||||
created_at,
|
created_at,
|
||||||
@@ -155,7 +155,7 @@ SELECT
|
|||||||
organization_id,
|
organization_id,
|
||||||
trust_center_access_id,
|
trust_center_access_id,
|
||||||
document_id,
|
document_id,
|
||||||
report_id,
|
report_file_id,
|
||||||
trust_center_file_id,
|
trust_center_file_id,
|
||||||
status,
|
status,
|
||||||
created_at,
|
created_at,
|
||||||
@@ -196,12 +196,12 @@ LIMIT 1;
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tcda *TrustCenterDocumentAccess) LoadByTrustCenterAccessIDAndReportID(
|
func (tcda *TrustCenterDocumentAccess) LoadByTrustCenterAccessIDAndReportFileID(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
conn pg.Querier,
|
conn pg.Querier,
|
||||||
scope Scoper,
|
scope Scoper,
|
||||||
trustCenterAccessID gid.GID,
|
trustCenterAccessID gid.GID,
|
||||||
reportID gid.GID,
|
reportFileID gid.GID,
|
||||||
) error {
|
) error {
|
||||||
q := `
|
q := `
|
||||||
SELECT
|
SELECT
|
||||||
@@ -209,7 +209,7 @@ SELECT
|
|||||||
organization_id,
|
organization_id,
|
||||||
trust_center_access_id,
|
trust_center_access_id,
|
||||||
document_id,
|
document_id,
|
||||||
report_id,
|
report_file_id,
|
||||||
trust_center_file_id,
|
trust_center_file_id,
|
||||||
status,
|
status,
|
||||||
created_at,
|
created_at,
|
||||||
@@ -219,7 +219,7 @@ FROM
|
|||||||
WHERE
|
WHERE
|
||||||
%s
|
%s
|
||||||
AND trust_center_access_id = @trust_center_access_id
|
AND trust_center_access_id = @trust_center_access_id
|
||||||
AND report_id = @report_id
|
AND report_file_id = @report_file_id
|
||||||
LIMIT 1;
|
LIMIT 1;
|
||||||
`
|
`
|
||||||
|
|
||||||
@@ -227,7 +227,7 @@ LIMIT 1;
|
|||||||
|
|
||||||
args := pgx.StrictNamedArgs{
|
args := pgx.StrictNamedArgs{
|
||||||
"trust_center_access_id": trustCenterAccessID,
|
"trust_center_access_id": trustCenterAccessID,
|
||||||
"report_id": reportID,
|
"report_file_id": reportFileID,
|
||||||
}
|
}
|
||||||
maps.Copy(args, scope.SQLArguments())
|
maps.Copy(args, scope.SQLArguments())
|
||||||
|
|
||||||
@@ -262,7 +262,7 @@ INSERT INTO trust_center_document_accesses (
|
|||||||
organization_id,
|
organization_id,
|
||||||
trust_center_access_id,
|
trust_center_access_id,
|
||||||
document_id,
|
document_id,
|
||||||
report_id,
|
report_file_id,
|
||||||
trust_center_file_id,
|
trust_center_file_id,
|
||||||
status,
|
status,
|
||||||
created_at,
|
created_at,
|
||||||
@@ -273,7 +273,7 @@ INSERT INTO trust_center_document_accesses (
|
|||||||
@organization_id,
|
@organization_id,
|
||||||
@trust_center_access_id,
|
@trust_center_access_id,
|
||||||
@document_id,
|
@document_id,
|
||||||
@report_id,
|
@report_file_id,
|
||||||
@trust_center_file_id,
|
@trust_center_file_id,
|
||||||
@status::trust_center_document_access_status,
|
@status::trust_center_document_access_status,
|
||||||
@created_at,
|
@created_at,
|
||||||
@@ -287,7 +287,7 @@ INSERT INTO trust_center_document_accesses (
|
|||||||
"organization_id": tcda.OrganizationID,
|
"organization_id": tcda.OrganizationID,
|
||||||
"trust_center_access_id": tcda.TrustCenterAccessID,
|
"trust_center_access_id": tcda.TrustCenterAccessID,
|
||||||
"document_id": tcda.DocumentID,
|
"document_id": tcda.DocumentID,
|
||||||
"report_id": tcda.ReportID,
|
"report_file_id": tcda.ReportFileID,
|
||||||
"trust_center_file_id": tcda.TrustCenterFileID,
|
"trust_center_file_id": tcda.TrustCenterFileID,
|
||||||
"status": tcda.Status,
|
"status": tcda.Status,
|
||||||
"created_at": tcda.CreatedAt,
|
"created_at": tcda.CreatedAt,
|
||||||
@@ -300,7 +300,7 @@ INSERT INTO trust_center_document_accesses (
|
|||||||
if pgErr.Code == "23505" {
|
if pgErr.Code == "23505" {
|
||||||
switch pgErr.ConstraintName {
|
switch pgErr.ConstraintName {
|
||||||
case "trust_center_document_accesse_trust_center_access_id_docume_key",
|
case "trust_center_document_accesse_trust_center_access_id_docume_key",
|
||||||
"trust_center_document_accesse_trust_center_access_id_report_key",
|
"trust_center_document_accesses_trust_center_access_id_report_file_key",
|
||||||
"trust_center_document_accesses_trust_center_file_id_key":
|
"trust_center_document_accesses_trust_center_file_id_key":
|
||||||
return ErrResourceAlreadyExists
|
return ErrResourceAlreadyExists
|
||||||
}
|
}
|
||||||
@@ -496,7 +496,7 @@ all_items AS (
|
|||||||
SELECT
|
SELECT
|
||||||
d.id AS item_id,
|
d.id AS item_id,
|
||||||
d.id AS document_id,
|
d.id AS document_id,
|
||||||
NULL::text AS report_id,
|
NULL::text AS report_file_id,
|
||||||
NULL::text AS trust_center_file_id,
|
NULL::text AS trust_center_file_id,
|
||||||
d.created_at AS item_created_at,
|
d.created_at AS item_created_at,
|
||||||
d.updated_at AS item_updated_at
|
d.updated_at AS item_updated_at
|
||||||
@@ -508,23 +508,23 @@ all_items AS (
|
|||||||
UNION ALL
|
UNION ALL
|
||||||
|
|
||||||
SELECT
|
SELECT
|
||||||
r.report_id AS item_id,
|
r.report_file_id AS item_id,
|
||||||
NULL::text AS document_id,
|
NULL::text AS document_id,
|
||||||
r.report_id AS report_id,
|
r.report_file_id AS report_file_id,
|
||||||
NULL::text AS trust_center_file_id,
|
NULL::text AS trust_center_file_id,
|
||||||
r.created_at AS item_created_at,
|
r.created_at AS item_created_at,
|
||||||
r.updated_at AS item_updated_at
|
r.updated_at AS item_updated_at
|
||||||
FROM audits r, tenant_organization o
|
FROM audits r, tenant_organization o
|
||||||
WHERE r.organization_id = o.organization_id
|
WHERE r.organization_id = o.organization_id
|
||||||
AND r.trust_center_visibility = 'PRIVATE'::trust_center_visibility
|
AND r.trust_center_visibility = 'PRIVATE'::trust_center_visibility
|
||||||
AND r.report_id IS NOT NULL
|
AND r.report_file_id IS NOT NULL
|
||||||
|
|
||||||
UNION ALL
|
UNION ALL
|
||||||
|
|
||||||
SELECT
|
SELECT
|
||||||
tcf.id AS item_id,
|
tcf.id AS item_id,
|
||||||
NULL::text AS document_id,
|
NULL::text AS document_id,
|
||||||
NULL::text AS report_id,
|
NULL::text AS report_file_id,
|
||||||
tcf.id AS trust_center_file_id,
|
tcf.id AS trust_center_file_id,
|
||||||
tcf.created_at AS item_created_at,
|
tcf.created_at AS item_created_at,
|
||||||
tcf.updated_at AS item_updated_at
|
tcf.updated_at AS item_updated_at
|
||||||
@@ -542,7 +542,7 @@ final_items AS (
|
|||||||
(SELECT organization_id FROM organization) AS organization_id,
|
(SELECT organization_id FROM organization) AS organization_id,
|
||||||
@trust_center_access_id AS trust_center_access_id,
|
@trust_center_access_id AS trust_center_access_id,
|
||||||
ai.document_id,
|
ai.document_id,
|
||||||
ai.report_id,
|
ai.report_file_id,
|
||||||
ai.trust_center_file_id,
|
ai.trust_center_file_id,
|
||||||
COALESCE(tcda.status, 'REQUESTED'::trust_center_document_access_status) AS status,
|
COALESCE(tcda.status, 'REQUESTED'::trust_center_document_access_status) AS status,
|
||||||
COALESCE(tcda.created_at, ai.item_created_at) AS created_at,
|
COALESCE(tcda.created_at, ai.item_created_at) AS created_at,
|
||||||
@@ -552,7 +552,7 @@ final_items AS (
|
|||||||
tcda.trust_center_access_id = @trust_center_access_id
|
tcda.trust_center_access_id = @trust_center_access_id
|
||||||
AND (
|
AND (
|
||||||
(tcda.document_id = ai.document_id AND ai.document_id IS NOT NULL)
|
(tcda.document_id = ai.document_id AND ai.document_id IS NOT NULL)
|
||||||
OR (tcda.report_id = ai.report_id AND ai.report_id IS NOT NULL)
|
OR (tcda.report_file_id = ai.report_file_id AND ai.report_file_id IS NOT NULL)
|
||||||
OR (tcda.trust_center_file_id = ai.trust_center_file_id AND ai.trust_center_file_id IS NOT NULL)
|
OR (tcda.trust_center_file_id = ai.trust_center_file_id AND ai.trust_center_file_id IS NOT NULL)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -562,7 +562,7 @@ SELECT
|
|||||||
organization_id,
|
organization_id,
|
||||||
trust_center_access_id,
|
trust_center_access_id,
|
||||||
document_id,
|
document_id,
|
||||||
report_id,
|
report_file_id,
|
||||||
trust_center_file_id,
|
trust_center_file_id,
|
||||||
status,
|
status,
|
||||||
created_at,
|
created_at,
|
||||||
@@ -606,7 +606,7 @@ SELECT
|
|||||||
organization_id,
|
organization_id,
|
||||||
trust_center_access_id,
|
trust_center_access_id,
|
||||||
document_id,
|
document_id,
|
||||||
report_id,
|
report_file_id,
|
||||||
trust_center_file_id,
|
trust_center_file_id,
|
||||||
status,
|
status,
|
||||||
created_at,
|
created_at,
|
||||||
@@ -714,12 +714,12 @@ WHERE
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GrantByReportIDs(
|
func GrantByReportFileIDs(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
conn pg.Querier,
|
conn pg.Querier,
|
||||||
scope Scoper,
|
scope Scoper,
|
||||||
trustCenterAccessID gid.GID,
|
trustCenterAccessID gid.GID,
|
||||||
reportIDs []gid.GID,
|
reportFileIDs []gid.GID,
|
||||||
updatedAt time.Time,
|
updatedAt time.Time,
|
||||||
) error {
|
) error {
|
||||||
q := `
|
q := `
|
||||||
@@ -728,32 +728,32 @@ SET status = 'GRANTED'::trust_center_document_access_status, updated_at = @updat
|
|||||||
WHERE
|
WHERE
|
||||||
%s
|
%s
|
||||||
AND trust_center_access_id = @trust_center_access_id
|
AND trust_center_access_id = @trust_center_access_id
|
||||||
AND report_id = ANY(@report_ids)
|
AND report_file_id = ANY(@report_file_ids)
|
||||||
`
|
`
|
||||||
|
|
||||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||||
|
|
||||||
args := pgx.StrictNamedArgs{
|
args := pgx.StrictNamedArgs{
|
||||||
"trust_center_access_id": trustCenterAccessID,
|
"trust_center_access_id": trustCenterAccessID,
|
||||||
"report_ids": reportIDs,
|
"report_file_ids": reportFileIDs,
|
||||||
"updated_at": updatedAt,
|
"updated_at": updatedAt,
|
||||||
}
|
}
|
||||||
maps.Copy(args, scope.SQLArguments())
|
maps.Copy(args, scope.SQLArguments())
|
||||||
|
|
||||||
_, err := conn.Exec(ctx, q, args)
|
_, err := conn.Exec(ctx, q, args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot grant trust center document accesses by report IDs: %w", err)
|
return fmt.Errorf("cannot grant trust center document accesses by report file IDs: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func RejectOrRevokeByReportIDs(
|
func RejectOrRevokeByReportFileIDs(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
conn pg.Querier,
|
conn pg.Querier,
|
||||||
scope Scoper,
|
scope Scoper,
|
||||||
trustCenterAccessID gid.GID,
|
trustCenterAccessID gid.GID,
|
||||||
reportIDs []gid.GID,
|
reportFileIDs []gid.GID,
|
||||||
updatedAt time.Time,
|
updatedAt time.Time,
|
||||||
) error {
|
) error {
|
||||||
q := `
|
q := `
|
||||||
@@ -767,21 +767,21 @@ SET
|
|||||||
WHERE
|
WHERE
|
||||||
%s
|
%s
|
||||||
AND trust_center_access_id = @trust_center_access_id
|
AND trust_center_access_id = @trust_center_access_id
|
||||||
AND report_id = ANY(@report_ids)
|
AND report_file_id = ANY(@report_file_ids)
|
||||||
`
|
`
|
||||||
|
|
||||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||||
|
|
||||||
args := pgx.StrictNamedArgs{
|
args := pgx.StrictNamedArgs{
|
||||||
"trust_center_access_id": trustCenterAccessID,
|
"trust_center_access_id": trustCenterAccessID,
|
||||||
"report_ids": reportIDs,
|
"report_file_ids": reportFileIDs,
|
||||||
"updated_at": updatedAt,
|
"updated_at": updatedAt,
|
||||||
}
|
}
|
||||||
maps.Copy(args, scope.SQLArguments())
|
maps.Copy(args, scope.SQLArguments())
|
||||||
|
|
||||||
_, err := conn.Exec(ctx, q, args)
|
_, err := conn.Exec(ctx, q, args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot reject trust center document accesses by report IDs: %w", err)
|
return fmt.Errorf("cannot reject trust center document accesses by report file IDs: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -829,7 +829,7 @@ WHEN NOT MATCHED
|
|||||||
organization_id,
|
organization_id,
|
||||||
trust_center_access_id,
|
trust_center_access_id,
|
||||||
document_id,
|
document_id,
|
||||||
report_id,
|
report_file_id,
|
||||||
trust_center_file_id,
|
trust_center_file_id,
|
||||||
status,
|
status,
|
||||||
created_at,
|
created_at,
|
||||||
@@ -887,7 +887,7 @@ WITH document_access_data AS (
|
|||||||
@organization_id AS organization_id,
|
@organization_id AS organization_id,
|
||||||
@trust_center_access_id AS trust_center_access_id,
|
@trust_center_access_id AS trust_center_access_id,
|
||||||
unnest(@document_ids::text[]) AS document_id,
|
unnest(@document_ids::text[]) AS document_id,
|
||||||
null::text AS report_id,
|
null::text AS report_file_id,
|
||||||
null::text AS trust_center_file_id,
|
null::text AS trust_center_file_id,
|
||||||
@status::trust_center_document_access_status AS status,
|
@status::trust_center_document_access_status AS status,
|
||||||
@created_at::timestamptz AS created_at,
|
@created_at::timestamptz AS created_at,
|
||||||
@@ -899,7 +899,7 @@ INSERT INTO trust_center_document_accesses (
|
|||||||
organization_id,
|
organization_id,
|
||||||
trust_center_access_id,
|
trust_center_access_id,
|
||||||
document_id,
|
document_id,
|
||||||
report_id,
|
report_file_id,
|
||||||
trust_center_file_id,
|
trust_center_file_id,
|
||||||
status,
|
status,
|
||||||
created_at,
|
created_at,
|
||||||
@@ -927,7 +927,7 @@ ON CONFLICT DO NOTHING
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tcdas TrustCenterDocumentAccesses) MergeReportAccesses(
|
func (tcdas TrustCenterDocumentAccesses) MergeReportFileAccesses(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
conn pg.Querier,
|
conn pg.Querier,
|
||||||
scope Scoper,
|
scope Scoper,
|
||||||
@@ -947,7 +947,7 @@ WITH data AS (
|
|||||||
)
|
)
|
||||||
MERGE INTO trust_center_document_accesses AS tcda
|
MERGE INTO trust_center_document_accesses AS tcda
|
||||||
USING data
|
USING data
|
||||||
ON data.id = tcda.report_id
|
ON data.id = tcda.report_file_id
|
||||||
AND tcda.tenant_id = @tenant_id
|
AND tcda.tenant_id = @tenant_id
|
||||||
AND tcda.trust_center_access_id = @trust_center_access_id
|
AND tcda.trust_center_access_id = @trust_center_access_id
|
||||||
WHEN MATCHED
|
WHEN MATCHED
|
||||||
@@ -955,7 +955,7 @@ WHEN MATCHED
|
|||||||
WHEN NOT MATCHED BY SOURCE
|
WHEN NOT MATCHED BY SOURCE
|
||||||
AND tcda.tenant_id = @tenant_id
|
AND tcda.tenant_id = @tenant_id
|
||||||
AND tcda.trust_center_access_id = @trust_center_access_id
|
AND tcda.trust_center_access_id = @trust_center_access_id
|
||||||
AND tcda.report_id IS NOT NULL
|
AND tcda.report_file_id IS NOT NULL
|
||||||
THEN DELETE
|
THEN DELETE
|
||||||
WHEN NOT MATCHED
|
WHEN NOT MATCHED
|
||||||
THEN INSERT (
|
THEN INSERT (
|
||||||
@@ -964,7 +964,7 @@ WHEN NOT MATCHED
|
|||||||
organization_id,
|
organization_id,
|
||||||
trust_center_access_id,
|
trust_center_access_id,
|
||||||
document_id,
|
document_id,
|
||||||
report_id,
|
report_file_id,
|
||||||
trust_center_file_id,
|
trust_center_file_id,
|
||||||
status,
|
status,
|
||||||
created_at,
|
created_at,
|
||||||
@@ -1000,29 +1000,29 @@ WHEN NOT MATCHED
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tcdas TrustCenterDocumentAccesses) BulkInsertReportAccesses(
|
func (tcdas TrustCenterDocumentAccesses) BulkInsertReportFileAccesses(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
conn pg.Querier,
|
conn pg.Querier,
|
||||||
scope Scoper,
|
scope Scoper,
|
||||||
trustCenterAccessID gid.GID,
|
trustCenterAccessID gid.GID,
|
||||||
organizationID gid.GID,
|
organizationID gid.GID,
|
||||||
reportIDs []gid.GID,
|
reportFileIDs []gid.GID,
|
||||||
status TrustCenterDocumentAccessStatus,
|
status TrustCenterDocumentAccessStatus,
|
||||||
createdAt time.Time,
|
createdAt time.Time,
|
||||||
) error {
|
) error {
|
||||||
if len(reportIDs) == 0 {
|
if len(reportFileIDs) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
q := `
|
q := `
|
||||||
WITH report_access_data AS (
|
WITH report_file_access_data AS (
|
||||||
SELECT
|
SELECT
|
||||||
generate_gid(decode_base64_unpadded(@tenant_id), @trust_center_document_access_entity_type) AS id,
|
generate_gid(decode_base64_unpadded(@tenant_id), @trust_center_document_access_entity_type) AS id,
|
||||||
@tenant_id AS tenant_id,
|
@tenant_id AS tenant_id,
|
||||||
@organization_id AS organization_id,
|
@organization_id AS organization_id,
|
||||||
@trust_center_access_id AS trust_center_access_id,
|
@trust_center_access_id AS trust_center_access_id,
|
||||||
null::text AS document_id,
|
null::text AS document_id,
|
||||||
unnest(@report_ids::text[]) AS report_id,
|
unnest(@report_file_ids::text[]) AS report_file_id,
|
||||||
null::text AS trust_center_file_id,
|
null::text AS trust_center_file_id,
|
||||||
@status::trust_center_document_access_status AS status,
|
@status::trust_center_document_access_status AS status,
|
||||||
@created_at::timestamptz AS created_at,
|
@created_at::timestamptz AS created_at,
|
||||||
@@ -1034,13 +1034,13 @@ INSERT INTO trust_center_document_accesses (
|
|||||||
organization_id,
|
organization_id,
|
||||||
trust_center_access_id,
|
trust_center_access_id,
|
||||||
document_id,
|
document_id,
|
||||||
report_id,
|
report_file_id,
|
||||||
trust_center_file_id,
|
trust_center_file_id,
|
||||||
status,
|
status,
|
||||||
created_at,
|
created_at,
|
||||||
updated_at
|
updated_at
|
||||||
)
|
)
|
||||||
SELECT * FROM report_access_data
|
SELECT * FROM report_file_access_data
|
||||||
ON CONFLICT DO NOTHING
|
ON CONFLICT DO NOTHING
|
||||||
`
|
`
|
||||||
|
|
||||||
@@ -1049,14 +1049,14 @@ ON CONFLICT DO NOTHING
|
|||||||
"organization_id": organizationID,
|
"organization_id": organizationID,
|
||||||
"trust_center_document_access_entity_type": TrustCenterDocumentAccessEntityType,
|
"trust_center_document_access_entity_type": TrustCenterDocumentAccessEntityType,
|
||||||
"trust_center_access_id": trustCenterAccessID,
|
"trust_center_access_id": trustCenterAccessID,
|
||||||
"report_ids": reportIDs,
|
"report_file_ids": reportFileIDs,
|
||||||
"status": status,
|
"status": status,
|
||||||
"created_at": createdAt,
|
"created_at": createdAt,
|
||||||
"updated_at": createdAt,
|
"updated_at": createdAt,
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := conn.Exec(ctx, q, args); err != nil {
|
if _, err := conn.Exec(ctx, q, args); err != nil {
|
||||||
return fmt.Errorf("cannot bulk insert trust center report accesses: %w", err)
|
return fmt.Errorf("cannot bulk insert trust center report file accesses: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -1075,7 +1075,7 @@ SELECT
|
|||||||
organization_id,
|
organization_id,
|
||||||
trust_center_access_id,
|
trust_center_access_id,
|
||||||
document_id,
|
document_id,
|
||||||
report_id,
|
report_file_id,
|
||||||
trust_center_file_id,
|
trust_center_file_id,
|
||||||
status,
|
status,
|
||||||
created_at,
|
created_at,
|
||||||
@@ -1226,7 +1226,7 @@ WHEN NOT MATCHED
|
|||||||
organization_id,
|
organization_id,
|
||||||
trust_center_access_id,
|
trust_center_access_id,
|
||||||
document_id,
|
document_id,
|
||||||
report_id,
|
report_file_id,
|
||||||
trust_center_file_id,
|
trust_center_file_id,
|
||||||
status,
|
status,
|
||||||
created_at,
|
created_at,
|
||||||
@@ -1280,7 +1280,7 @@ WITH trust_center_file_access_data AS (
|
|||||||
@organization_id AS organization_id,
|
@organization_id AS organization_id,
|
||||||
@trust_center_access_id AS trust_center_access_id,
|
@trust_center_access_id AS trust_center_access_id,
|
||||||
null::text AS document_id,
|
null::text AS document_id,
|
||||||
null::text AS report_id,
|
null::text AS report_file_id,
|
||||||
unnest(@trust_center_file_ids::text[]) AS trust_center_file_id,
|
unnest(@trust_center_file_ids::text[]) AS trust_center_file_id,
|
||||||
@status::trust_center_document_access_status AS status,
|
@status::trust_center_document_access_status AS status,
|
||||||
@created_at::timestamptz AS created_at,
|
@created_at::timestamptz AS created_at,
|
||||||
@@ -1292,7 +1292,7 @@ INSERT INTO trust_center_document_accesses (
|
|||||||
organization_id,
|
organization_id,
|
||||||
trust_center_access_id,
|
trust_center_access_id,
|
||||||
document_id,
|
document_id,
|
||||||
report_id,
|
report_file_id,
|
||||||
trust_center_file_id,
|
trust_center_file_id,
|
||||||
status,
|
status,
|
||||||
created_at,
|
created_at,
|
||||||
|
|||||||
@@ -19,8 +19,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
|
||||||
"go.gearno.de/crypto/uuid"
|
|
||||||
"go.gearno.de/kit/pg"
|
"go.gearno.de/kit/pg"
|
||||||
"go.probo.inc/probo/pkg/coredata"
|
"go.probo.inc/probo/pkg/coredata"
|
||||||
"go.probo.inc/probo/pkg/filevalidation"
|
"go.probo.inc/probo/pkg/filevalidation"
|
||||||
@@ -113,7 +111,11 @@ func (s AuditService) Get(
|
|||||||
err := s.svc.pg.WithConn(
|
err := s.svc.pg.WithConn(
|
||||||
ctx,
|
ctx,
|
||||||
func(ctx context.Context, conn pg.Querier) error {
|
func(ctx context.Context, conn pg.Querier) error {
|
||||||
return audit.LoadByID(ctx, conn, scope, auditID)
|
if err := audit.LoadByID(ctx, conn, scope, auditID); err != nil {
|
||||||
|
return fmt.Errorf("cannot load audit: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -123,16 +125,20 @@ func (s AuditService) Get(
|
|||||||
return audit, nil
|
return audit, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s AuditService) GetByReportID(
|
func (s AuditService) GetByReportFileID(
|
||||||
ctx context.Context, scope coredata.Scoper,
|
ctx context.Context, scope coredata.Scoper,
|
||||||
reportID gid.GID,
|
fileID gid.GID,
|
||||||
) (*coredata.Audit, error) {
|
) (*coredata.Audit, error) {
|
||||||
audit := &coredata.Audit{}
|
audit := &coredata.Audit{}
|
||||||
|
|
||||||
err := s.svc.pg.WithConn(
|
err := s.svc.pg.WithConn(
|
||||||
ctx,
|
ctx,
|
||||||
func(ctx context.Context, conn pg.Querier) error {
|
func(ctx context.Context, conn pg.Querier) error {
|
||||||
return audit.LoadByReportID(ctx, conn, scope, reportID)
|
if err := audit.LoadByReportFileID(ctx, conn, scope, fileID); err != nil {
|
||||||
|
return fmt.Errorf("cannot load report file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -326,7 +332,7 @@ func (s AuditService) CountForOrganizationID(
|
|||||||
|
|
||||||
func (s AuditService) UploadReport(
|
func (s AuditService) UploadReport(
|
||||||
ctx context.Context, scope coredata.Scoper,
|
ctx context.Context, scope coredata.Scoper,
|
||||||
req UploadAuditReportRequest,
|
req *UploadAuditReportRequest,
|
||||||
) (*coredata.Audit, error) {
|
) (*coredata.Audit, error) {
|
||||||
if err := req.Validate(); err != nil {
|
if err := req.Validate(); err != nil {
|
||||||
return nil, fmt.Errorf("invalid request: %w", err)
|
return nil, fmt.Errorf("invalid request: %w", err)
|
||||||
@@ -334,60 +340,52 @@ func (s AuditService) UploadReport(
|
|||||||
|
|
||||||
audit := &coredata.Audit{}
|
audit := &coredata.Audit{}
|
||||||
|
|
||||||
err := s.svc.pg.WithTx(
|
err := s.svc.pg.WithConn(
|
||||||
|
ctx,
|
||||||
|
func(ctx context.Context, conn pg.Querier) error {
|
||||||
|
err := audit.LoadByID(ctx, conn, scope, req.AuditID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot load audit: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fv := filevalidation.NewValidator(
|
||||||
|
filevalidation.WithCategories(filevalidation.CategoryDocument),
|
||||||
|
filevalidation.WithMaxFileSize(25*1024*1024),
|
||||||
|
)
|
||||||
|
|
||||||
|
file, err := s.svc.Files.UploadAndSaveFile(
|
||||||
|
ctx, scope,
|
||||||
|
fv,
|
||||||
|
map[string]string{"organization-id": audit.OrganizationID.String()},
|
||||||
|
&FileUpload{
|
||||||
|
Content: req.File.Content,
|
||||||
|
Filename: req.File.Filename,
|
||||||
|
Size: req.File.Size,
|
||||||
|
ContentType: req.File.ContentType,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("cannot upload file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.svc.pg.WithTx(
|
||||||
ctx,
|
ctx,
|
||||||
func(ctx context.Context, conn pg.Tx) error {
|
func(ctx context.Context, conn pg.Tx) error {
|
||||||
if err := audit.LoadByID(ctx, conn, scope, req.AuditID); err != nil {
|
if err := audit.LoadByID(ctx, conn, scope, req.AuditID); err != nil {
|
||||||
return fmt.Errorf("cannot load audit: %w", err)
|
return fmt.Errorf("cannot load audit: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
reportID := gid.New(scope.GetTenantID(), coredata.ReportEntityType)
|
audit.ReportFileID = &file.ID
|
||||||
now := time.Now()
|
|
||||||
|
|
||||||
objectKey, err := uuid.NewV7()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("cannot generate object key: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = s.svc.s3.PutObject(ctx, &s3.PutObjectInput{
|
|
||||||
Bucket: new(s.svc.bucket),
|
|
||||||
Key: new(objectKey.String()),
|
|
||||||
Body: req.File.Content,
|
|
||||||
ContentType: new(req.File.ContentType),
|
|
||||||
CacheControl: new("private, max-age=3600"),
|
|
||||||
Metadata: map[string]string{
|
|
||||||
"type": "report",
|
|
||||||
"report-id": reportID.String(),
|
|
||||||
"organization-id": audit.OrganizationID.String(),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("cannot upload report to S3: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
report := &coredata.Report{
|
|
||||||
ID: reportID,
|
|
||||||
OrganizationID: audit.OrganizationID,
|
|
||||||
ObjectKey: objectKey.String(),
|
|
||||||
MimeType: req.File.ContentType,
|
|
||||||
Filename: req.File.Filename,
|
|
||||||
Size: req.File.Size,
|
|
||||||
CreatedAt: now,
|
|
||||||
UpdatedAt: now,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := report.Insert(ctx, conn, scope); err != nil {
|
|
||||||
return fmt.Errorf("cannot insert report: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
audit.ReportID = &report.ID
|
|
||||||
audit.UpdatedAt = time.Now()
|
audit.UpdatedAt = time.Now()
|
||||||
|
|
||||||
if err := audit.Update(ctx, conn, scope); err != nil {
|
return audit.Update(ctx, conn, scope)
|
||||||
return fmt.Errorf("cannot update audit: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -407,16 +405,16 @@ func (s AuditService) GenerateReportURL(
|
|||||||
return nil, fmt.Errorf("cannot get audit: %w", err)
|
return nil, fmt.Errorf("cannot get audit: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if audit.ReportID == nil {
|
if audit.ReportFileID == nil {
|
||||||
return nil, fmt.Errorf("audit has no report")
|
return nil, fmt.Errorf("audit has no report")
|
||||||
}
|
}
|
||||||
|
|
||||||
url, err := s.svc.Reports.GenerateDownloadURL(ctx, scope, *audit.ReportID, expiresIn)
|
url, err := s.svc.Files.GenerateFileTempURL(ctx, scope, *audit.ReportFileID, expiresIn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot generate report download URL: %w", err)
|
return nil, fmt.Errorf("cannot generate report download URL: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return url, nil
|
return &url, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s AuditService) DeleteReport(
|
func (s AuditService) DeleteReport(
|
||||||
@@ -425,21 +423,21 @@ func (s AuditService) DeleteReport(
|
|||||||
) (*coredata.Audit, error) {
|
) (*coredata.Audit, error) {
|
||||||
audit := &coredata.Audit{}
|
audit := &coredata.Audit{}
|
||||||
|
|
||||||
err := s.svc.pg.WithTx(
|
return audit, s.svc.pg.WithTx(
|
||||||
ctx,
|
ctx,
|
||||||
func(ctx context.Context, conn pg.Tx) error {
|
func(ctx context.Context, conn pg.Tx) error {
|
||||||
if err := audit.LoadByID(ctx, conn, scope, auditID); err != nil {
|
if err := audit.LoadByID(ctx, conn, scope, auditID); err != nil {
|
||||||
return fmt.Errorf("cannot load audit: %w", err)
|
return fmt.Errorf("cannot load audit: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if audit.ReportID != nil {
|
if audit.ReportFileID != nil {
|
||||||
report := &coredata.Report{ID: *audit.ReportID}
|
file := coredata.File{ID: *audit.ReportFileID}
|
||||||
|
|
||||||
if err := report.Delete(ctx, conn, scope); err != nil {
|
if err := file.SoftDelete(ctx, conn, scope); err != nil {
|
||||||
return fmt.Errorf("cannot delete report: %w", err)
|
return fmt.Errorf("cannot soft-delete report file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
audit.ReportID = nil
|
audit.ReportFileID = nil
|
||||||
audit.UpdatedAt = time.Now()
|
audit.UpdatedAt = time.Now()
|
||||||
|
|
||||||
if err := audit.Update(ctx, conn, scope); err != nil {
|
if err := audit.Update(ctx, conn, scope); err != nil {
|
||||||
@@ -450,11 +448,6 @@ func (s AuditService) DeleteReport(
|
|||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return audit, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s AuditService) ListForControlID(
|
func (s AuditService) ListForControlID(
|
||||||
|
|||||||
@@ -1,131 +0,0 @@
|
|||||||
// Copyright (c) 2025-2026 Probo Inc <hello@getprobo.com>.
|
|
||||||
//
|
|
||||||
// Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
// purpose with or without fee is hereby granted, provided that the above
|
|
||||||
// copyright notice and this permission notice appear in all copies.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
||||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
||||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
||||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
||||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
||||||
// PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
|
|
||||||
package probo
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
|
||||||
"go.gearno.de/kit/pg"
|
|
||||||
"go.probo.inc/probo/pkg/coredata"
|
|
||||||
"go.probo.inc/probo/pkg/gid"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ReportService struct {
|
|
||||||
svc *Service
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s ReportService) Get(
|
|
||||||
ctx context.Context, scope coredata.Scoper,
|
|
||||||
reportID gid.GID,
|
|
||||||
) (*coredata.Report, error) {
|
|
||||||
report := &coredata.Report{}
|
|
||||||
|
|
||||||
err := s.svc.pg.WithConn(
|
|
||||||
ctx,
|
|
||||||
func(ctx context.Context, conn pg.Querier) error {
|
|
||||||
err := report.LoadByID(ctx, conn, scope, reportID)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("cannot load report: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return report, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s ReportService) GetByIDs(
|
|
||||||
ctx context.Context, scope coredata.Scoper,
|
|
||||||
reportIDs ...gid.GID,
|
|
||||||
) (coredata.Reports, error) {
|
|
||||||
var reports coredata.Reports
|
|
||||||
|
|
||||||
err := s.svc.pg.WithConn(
|
|
||||||
ctx,
|
|
||||||
func(ctx context.Context, conn pg.Querier) error {
|
|
||||||
if err := reports.LoadByIDs(
|
|
||||||
ctx,
|
|
||||||
conn,
|
|
||||||
scope,
|
|
||||||
reportIDs,
|
|
||||||
); err != nil {
|
|
||||||
return fmt.Errorf("cannot load reports by ids: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return reports, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s ReportService) Delete(
|
|
||||||
ctx context.Context, scope coredata.Scoper,
|
|
||||||
reportID gid.GID,
|
|
||||||
) error {
|
|
||||||
return s.svc.pg.WithTx(ctx, func(ctx context.Context, conn pg.Tx) error {
|
|
||||||
report := &coredata.Report{}
|
|
||||||
|
|
||||||
err := report.LoadByID(ctx, conn, scope, reportID)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("cannot get report: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = report.Delete(ctx, conn, scope)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("cannot delete report: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s ReportService) GenerateDownloadURL(
|
|
||||||
ctx context.Context, scope coredata.Scoper,
|
|
||||||
reportID gid.GID,
|
|
||||||
expiresIn time.Duration,
|
|
||||||
) (*string, error) {
|
|
||||||
report, err := s.Get(ctx, scope, reportID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("cannot get report: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
presignClient := s3.NewPresignClient(s.svc.s3)
|
|
||||||
|
|
||||||
presignedReq, err := presignClient.PresignGetObject(ctx, &s3.GetObjectInput{
|
|
||||||
Bucket: new(s.svc.bucket),
|
|
||||||
Key: new(report.ObjectKey),
|
|
||||||
ResponseCacheControl: new("max-age=3600, public"),
|
|
||||||
ResponseContentType: new(report.MimeType),
|
|
||||||
ResponseContentDisposition: new(fmt.Sprintf("attachment; filename=\"%s\"", report.Filename)),
|
|
||||||
}, func(opts *s3.PresignOptions) {
|
|
||||||
opts.Expires = expiresIn
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("cannot presign GetObject request: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &presignedReq.URL, nil
|
|
||||||
}
|
|
||||||
@@ -106,7 +106,6 @@ type (
|
|||||||
Data *DatumService
|
Data *DatumService
|
||||||
Audits *AuditService
|
Audits *AuditService
|
||||||
WebhookSubscriptions *WebhookSubscriptionService
|
WebhookSubscriptions *WebhookSubscriptionService
|
||||||
Reports *ReportService
|
|
||||||
TrustCenters *TrustCenterService
|
TrustCenters *TrustCenterService
|
||||||
TrustCenterAccesses *TrustCenterAccessService
|
TrustCenterAccesses *TrustCenterAccessService
|
||||||
TrustCenterReferences *TrustCenterReferenceService
|
TrustCenterReferences *TrustCenterReferenceService
|
||||||
@@ -230,7 +229,6 @@ func NewService(
|
|||||||
svc.Data = &DatumService{svc: svc}
|
svc.Data = &DatumService{svc: svc}
|
||||||
svc.Audits = &AuditService{svc: svc}
|
svc.Audits = &AuditService{svc: svc}
|
||||||
svc.WebhookSubscriptions = &WebhookSubscriptionService{svc: svc}
|
svc.WebhookSubscriptions = &WebhookSubscriptionService{svc: svc}
|
||||||
svc.Reports = &ReportService{svc: svc}
|
|
||||||
svc.TrustCenters = &TrustCenterService{svc: svc}
|
svc.TrustCenters = &TrustCenterService{svc: svc}
|
||||||
svc.TrustCenterAccesses = &TrustCenterAccessService{svc: svc}
|
svc.TrustCenterAccesses = &TrustCenterAccessService{svc: svc}
|
||||||
svc.TrustCenterReferences = &TrustCenterReferenceService{svc: svc}
|
svc.TrustCenterReferences = &TrustCenterReferenceService{svc: svc}
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ func (utcar *UpdateTrustCenterAccessRequest) Validate() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for i, reportAccess := range utcar.ReportAccesses {
|
for i, reportAccess := range utcar.ReportAccesses {
|
||||||
v.Check(reportAccess.ID, fmt.Sprintf("reportAccesses[%d].ID", i), validator.Required(), validator.GID(coredata.ReportEntityType))
|
v.Check(reportAccess.ID, fmt.Sprintf("reportAccesses[%d].ID", i), validator.Required(), validator.GID(coredata.FileEntityType))
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, reportAccess := range utcar.TrustCenterFileAccesses {
|
for i, reportAccess := range utcar.TrustCenterFileAccesses {
|
||||||
@@ -263,7 +263,7 @@ func (s TrustCenterAccessService) Update(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := tcdas.MergeReportAccesses(ctx, tx, scope, access.OrganizationID, access.ID, reportData); err != nil {
|
if err := tcdas.MergeReportFileAccesses(ctx, tx, scope, access.OrganizationID, access.ID, reportData); err != nil {
|
||||||
return fmt.Errorf("cannot merge report accesses: %w", err)
|
return fmt.Errorf("cannot merge report accesses: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ package console_v1
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/vikstrous/dataloadgen"
|
"github.com/vikstrous/dataloadgen"
|
||||||
"go.gearno.de/kit/log"
|
"go.gearno.de/kit/log"
|
||||||
@@ -68,50 +67,30 @@ func (r *auditResolver) Framework(ctx context.Context, obj *types.Audit) (*types
|
|||||||
return types.NewFramework(framework), nil
|
return types.NewFramework(framework), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Report is the resolver for the report field.
|
// ReportFile is the resolver for the reportFile field.
|
||||||
func (r *auditResolver) Report(ctx context.Context, obj *types.Audit) (*types.Report, error) {
|
func (r *auditResolver) ReportFile(ctx context.Context, obj *types.Audit) (*types.File, error) {
|
||||||
if _, err := r.authorize(ctx, obj.ID, probo.ActionReportGet); err != nil {
|
if _, err := r.authorize(ctx, obj.ID, probo.ActionReportGet); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if obj.Report == nil {
|
if obj.ReportFile == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
loaders := dataloader.FromContext(ctx)
|
loaders := dataloader.FromContext(ctx)
|
||||||
|
|
||||||
report, err := loaders.Report.Load(ctx, obj.Report.ID)
|
file, err := loaders.File.Load(ctx, obj.ReportFile.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, dataloadgen.ErrNotFound) {
|
if errors.Is(err, dataloadgen.ErrNotFound) {
|
||||||
return nil, gqlutils.NotFound(ctx, err)
|
return nil, gqlutils.NotFound(ctx, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
r.logger.ErrorCtx(ctx, "cannot load report", log.Error(err))
|
r.logger.ErrorCtx(ctx, "cannot load report file", log.Error(err))
|
||||||
|
|
||||||
return nil, gqlutils.Internal(ctx)
|
return nil, gqlutils.Internal(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
return types.NewReport(report), nil
|
return types.NewFile(file), nil
|
||||||
}
|
|
||||||
|
|
||||||
// ReportURL is the resolver for the reportUrl field.
|
|
||||||
func (r *auditResolver) ReportURL(ctx context.Context, obj *types.Audit) (*string, error) {
|
|
||||||
scope, err := r.authorize(ctx, obj.ID, probo.ActionReportGetReportUrl)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if obj.Report == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
url, err := r.probo.Audits.GenerateReportURL(ctx, scope, obj.ID, 15*time.Minute)
|
|
||||||
if err != nil {
|
|
||||||
r.logger.ErrorCtx(ctx, "cannot generate report URL", log.Error(err))
|
|
||||||
return nil, gqlutils.Internal(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
return url, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Controls is the resolver for the controls field.
|
// Controls is the resolver for the controls field.
|
||||||
@@ -431,7 +410,7 @@ func (r *mutationResolver) CreateAudit(ctx context.Context, input types.CreateAu
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
audit, err = r.probo.Audits.UploadReport(ctx, scope, uploadReq)
|
audit, err = r.probo.Audits.UploadReport(ctx, scope, &uploadReq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
|
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
|
||||||
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
|
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
|
||||||
@@ -514,7 +493,7 @@ func (r *mutationResolver) UploadAuditReport(ctx context.Context, input types.Up
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
audit, err := r.probo.Audits.UploadReport(ctx, scope, req)
|
audit, err := r.probo.Audits.UploadReport(ctx, scope, &req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
|
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
|
||||||
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
|
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
|
||||||
@@ -704,43 +683,6 @@ func (r *mutationResolver) PublishFindingList(ctx context.Context, input types.P
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DownloadURL is the resolver for the downloadUrl field.
|
|
||||||
func (r *reportResolver) DownloadURL(ctx context.Context, obj *types.Report) (*string, error) {
|
|
||||||
scope, err := r.authorize(ctx, obj.ID, probo.ActionReportDownloadUrlGet)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
url, err := r.probo.Reports.GenerateDownloadURL(ctx, scope, obj.ID, 15*time.Minute)
|
|
||||||
if err != nil {
|
|
||||||
r.logger.ErrorCtx(ctx, "cannot generate download URL", log.Error(err))
|
|
||||||
return nil, gqlutils.Internal(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
return url, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Audit is the resolver for the audit field.
|
|
||||||
func (r *reportResolver) Audit(ctx context.Context, obj *types.Report) (*types.Audit, error) {
|
|
||||||
scope, err := r.authorize(ctx, obj.ID, probo.ActionAuditGet)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
audit, err := r.probo.Audits.GetByReportID(ctx, scope, obj.ID)
|
|
||||||
if err != nil {
|
|
||||||
r.logger.ErrorCtx(ctx, "cannot load audit for report", log.Error(err))
|
|
||||||
return nil, gqlutils.Internal(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
return types.NewAudit(audit), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Permission is the resolver for the permission field.
|
|
||||||
func (r *reportResolver) Permission(ctx context.Context, obj *types.Report, action string) (bool, error) {
|
|
||||||
return r.Resolver.Permission(ctx, obj, action)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Audit returns schema.AuditResolver implementation.
|
// Audit returns schema.AuditResolver implementation.
|
||||||
func (r *Resolver) Audit() schema.AuditResolver { return &auditResolver{r} }
|
func (r *Resolver) Audit() schema.AuditResolver { return &auditResolver{r} }
|
||||||
|
|
||||||
@@ -757,11 +699,7 @@ func (r *Resolver) FindingConnection() schema.FindingConnectionResolver {
|
|||||||
return &findingConnectionResolver{r}
|
return &findingConnectionResolver{r}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Report returns schema.ReportResolver implementation.
|
|
||||||
func (r *Resolver) Report() schema.ReportResolver { return &reportResolver{r} }
|
|
||||||
|
|
||||||
type auditResolver struct{ *Resolver }
|
type auditResolver struct{ *Resolver }
|
||||||
type auditConnectionResolver struct{ *Resolver }
|
type auditConnectionResolver struct{ *Resolver }
|
||||||
type findingResolver struct{ *Resolver }
|
type findingResolver struct{ *Resolver }
|
||||||
type findingConnectionResolver struct{ *Resolver }
|
type findingConnectionResolver struct{ *Resolver }
|
||||||
type reportResolver struct{ *Resolver }
|
|
||||||
|
|||||||
@@ -279,16 +279,6 @@ func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error
|
|||||||
|
|
||||||
return types.NewObligation(obligation), nil
|
return types.NewObligation(obligation), nil
|
||||||
}
|
}
|
||||||
case coredata.ReportEntityType:
|
|
||||||
action = probo.ActionReportGet
|
|
||||||
loadNode = func(ctx context.Context, scope *coredata.Scope, id gid.GID) (types.Node, error) {
|
|
||||||
report, err := r.probo.Reports.Get(ctx, scope, id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return types.NewReport(report), nil
|
|
||||||
}
|
|
||||||
case coredata.ProcessingActivityEntityType:
|
case coredata.ProcessingActivityEntityType:
|
||||||
action = probo.ActionProcessingActivityList
|
action = probo.ActionProcessingActivityList
|
||||||
loadNode = func(ctx context.Context, scope *coredata.Scope, id gid.GID) (types.Node, error) {
|
loadNode = func(ctx context.Context, scope *coredata.Scope, id gid.GID) (types.Node, error) {
|
||||||
|
|||||||
@@ -62,7 +62,6 @@ type (
|
|||||||
Measure *dataloadgen.Loader[gid.GID, *coredata.Measure]
|
Measure *dataloadgen.Loader[gid.GID, *coredata.Measure]
|
||||||
Task *dataloadgen.Loader[gid.GID, *coredata.Task]
|
Task *dataloadgen.Loader[gid.GID, *coredata.Task]
|
||||||
File *dataloadgen.Loader[gid.GID, *coredata.File]
|
File *dataloadgen.Loader[gid.GID, *coredata.File]
|
||||||
Report *dataloadgen.Loader[gid.GID, *coredata.Report]
|
|
||||||
CookieBanner *dataloadgen.Loader[gid.GID, *coredata.CookieBanner]
|
CookieBanner *dataloadgen.Loader[gid.GID, *coredata.CookieBanner]
|
||||||
CookieCategory *dataloadgen.Loader[gid.GID, *coredata.CookieCategory]
|
CookieCategory *dataloadgen.Loader[gid.GID, *coredata.CookieCategory]
|
||||||
CommonTrackerPattern *dataloadgen.Loader[gid.GID, *coredata.CommonTrackerPattern]
|
CommonTrackerPattern *dataloadgen.Loader[gid.GID, *coredata.CommonTrackerPattern]
|
||||||
@@ -114,7 +113,6 @@ func (f *batchFetcher) newLoaders() *Loaders {
|
|||||||
Measure: dataloadgen.NewMappedLoader(f.fetchMeasures),
|
Measure: dataloadgen.NewMappedLoader(f.fetchMeasures),
|
||||||
Task: dataloadgen.NewMappedLoader(f.fetchTasks),
|
Task: dataloadgen.NewMappedLoader(f.fetchTasks),
|
||||||
File: dataloadgen.NewMappedLoader(f.fetchFiles),
|
File: dataloadgen.NewMappedLoader(f.fetchFiles),
|
||||||
Report: dataloadgen.NewMappedLoader(f.fetchReports),
|
|
||||||
CookieBanner: dataloadgen.NewMappedLoader(f.fetchCookieBanners),
|
CookieBanner: dataloadgen.NewMappedLoader(f.fetchCookieBanners),
|
||||||
CookieCategory: dataloadgen.NewMappedLoader(f.fetchCookieCategories),
|
CookieCategory: dataloadgen.NewMappedLoader(f.fetchCookieCategories),
|
||||||
CommonTrackerPattern: dataloadgen.NewMappedLoader(f.fetchCommonTrackerPatterns),
|
CommonTrackerPattern: dataloadgen.NewMappedLoader(f.fetchCommonTrackerPatterns),
|
||||||
@@ -286,22 +284,6 @@ func (f *batchFetcher) fetchFiles(ctx context.Context, keys []gid.GID) (map[gid.
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *batchFetcher) fetchReports(ctx context.Context, keys []gid.GID) (map[gid.GID]*coredata.Report, error) {
|
|
||||||
scope := coredata.NewScopeFromObjectID(keys[0])
|
|
||||||
|
|
||||||
reports, err := f.probo.Reports.GetByIDs(ctx, scope, keys...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("cannot batch load reports: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
result := make(map[gid.GID]*coredata.Report, len(reports))
|
|
||||||
for _, v := range reports {
|
|
||||||
result[v.ID] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *batchFetcher) fetchCookieBanners(ctx context.Context, keys []gid.GID) (map[gid.GID]*coredata.CookieBanner, error) {
|
func (f *batchFetcher) fetchCookieBanners(ctx context.Context, keys []gid.GID) (map[gid.GID]*coredata.CookieBanner, error) {
|
||||||
scope := coredata.NewScopeFromObjectID(keys[0])
|
scope := coredata.NewScopeFromObjectID(keys[0])
|
||||||
|
|
||||||
|
|||||||
@@ -151,8 +151,7 @@ type Audit implements Node {
|
|||||||
framework: Framework @goField(forceResolver: true)
|
framework: Framework @goField(forceResolver: true)
|
||||||
validFrom: Datetime
|
validFrom: Datetime
|
||||||
validUntil: Datetime
|
validUntil: Datetime
|
||||||
report: Report @goField(forceResolver: true)
|
reportFile: File @goField(forceResolver: true)
|
||||||
reportUrl: String @goField(forceResolver: true)
|
|
||||||
state: AuditState!
|
state: AuditState!
|
||||||
|
|
||||||
controls(
|
controls(
|
||||||
@@ -208,20 +207,6 @@ type Finding implements Node {
|
|||||||
permission(action: String!): Boolean! @goField(forceResolver: true)
|
permission(action: String!): Boolean! @goField(forceResolver: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Report implements Node {
|
|
||||||
id: ID!
|
|
||||||
objectKey: String!
|
|
||||||
mimeType: String!
|
|
||||||
filename: String!
|
|
||||||
size: Int!
|
|
||||||
downloadUrl: String @goField(forceResolver: true)
|
|
||||||
createdAt: Datetime!
|
|
||||||
updatedAt: Datetime!
|
|
||||||
audit: Audit @goField(forceResolver: true)
|
|
||||||
|
|
||||||
permission(action: String!): Boolean! @goField(forceResolver: true)
|
|
||||||
}
|
|
||||||
|
|
||||||
type AuditConnection
|
type AuditConnection
|
||||||
@goModel(
|
@goModel(
|
||||||
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.AuditConnection"
|
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.AuditConnection"
|
||||||
|
|||||||
@@ -334,7 +334,8 @@ type TrustCenterDocumentAccess
|
|||||||
id: ID!
|
id: ID!
|
||||||
status: TrustCenterDocumentAccessStatus!
|
status: TrustCenterDocumentAccessStatus!
|
||||||
document: Document @goField(forceResolver: true)
|
document: Document @goField(forceResolver: true)
|
||||||
report: Report @goField(forceResolver: true)
|
reportFile: File @goField(forceResolver: true)
|
||||||
|
audit: Audit @goField(forceResolver: true)
|
||||||
trustCenterFile: TrustCenterFile @goField(forceResolver: true)
|
trustCenterFile: TrustCenterFile @goField(forceResolver: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1056,24 +1056,55 @@ func (r *trustCenterDocumentAccessResolver) Document(ctx context.Context, obj *t
|
|||||||
return types.NewDocument(document), nil
|
return types.NewDocument(document), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Report is the resolver for the report field.
|
// ReportFile is the resolver for the reportFile field.
|
||||||
func (r *trustCenterDocumentAccessResolver) Report(ctx context.Context, obj *types.TrustCenterDocumentAccess) (*types.Report, error) {
|
func (r *trustCenterDocumentAccessResolver) ReportFile(ctx context.Context, obj *types.TrustCenterDocumentAccess) (*types.File, error) {
|
||||||
scope, err := r.authorize(ctx, obj.TrustCenterAccessID, probo.ActionReportGet)
|
if _, err := r.authorize(ctx, obj.ID, probo.ActionFileGet); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if obj.ReportFile == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
loaders := dataloader.FromContext(ctx)
|
||||||
|
|
||||||
|
file, err := loaders.File.Load(ctx, obj.ReportFile.ID)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) {
|
||||||
|
return nil, gqlutils.NotFound(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
r.logger.ErrorCtx(ctx, "cannot load report file", log.Error(err))
|
||||||
|
|
||||||
|
return nil, gqlutils.Internal(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
return types.NewFile(file), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Audit is the resolver for the audit field.
|
||||||
|
func (r *trustCenterDocumentAccessResolver) Audit(ctx context.Context, obj *types.TrustCenterDocumentAccess) (*types.Audit, error) {
|
||||||
|
scope, err := r.authorize(ctx, obj.ID, probo.ActionAuditGet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if obj.ReportID == nil {
|
if obj.ReportFileID == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
report, err := r.probo.Reports.Get(ctx, scope, *obj.ReportID)
|
audit, err := r.probo.Audits.GetByReportFileID(ctx, scope, *obj.ReportFileID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.logger.ErrorCtx(ctx, "cannot load report", log.Error(err))
|
if errors.Is(err, coredata.ErrResourceNotFound) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
r.logger.ErrorCtx(ctx, "cannot load audit for report file", log.Error(err))
|
||||||
|
|
||||||
return nil, gqlutils.Internal(ctx)
|
return nil, gqlutils.Internal(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
return types.NewReport(report), nil
|
return types.NewAudit(audit), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TrustCenterFile is the resolver for the trustCenterFile field.
|
// TrustCenterFile is the resolver for the trustCenterFile field.
|
||||||
|
|||||||
@@ -77,9 +77,9 @@ func NewAudit(a *coredata.Audit) *Audit {
|
|||||||
UpdatedAt: a.UpdatedAt,
|
UpdatedAt: a.UpdatedAt,
|
||||||
}
|
}
|
||||||
|
|
||||||
if a.ReportID != nil {
|
if a.ReportFileID != nil {
|
||||||
node.Report = &Report{
|
node.ReportFile = &File{
|
||||||
ID: *a.ReportID,
|
ID: *a.ReportFileID,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
// Copyright (c) 2025-2026 Probo Inc <hello@getprobo.com>.
|
|
||||||
//
|
|
||||||
// Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
// purpose with or without fee is hereby granted, provided that the above
|
|
||||||
// copyright notice and this permission notice appear in all copies.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
||||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
||||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
||||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
||||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
||||||
// PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
|
|
||||||
package types
|
|
||||||
|
|
||||||
import (
|
|
||||||
"go.probo.inc/probo/pkg/coredata"
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewReport(r *coredata.Report) *Report {
|
|
||||||
return &Report{
|
|
||||||
ID: r.ID,
|
|
||||||
ObjectKey: r.ObjectKey,
|
|
||||||
MimeType: r.MimeType,
|
|
||||||
Filename: r.Filename,
|
|
||||||
Size: int(r.Size),
|
|
||||||
CreatedAt: r.CreatedAt,
|
|
||||||
UpdatedAt: r.UpdatedAt,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -42,13 +42,13 @@ type (
|
|||||||
UpdatedAt time.Time `json:"updatedAt"`
|
UpdatedAt time.Time `json:"updatedAt"`
|
||||||
TrustCenterAccess *TrustCenterAccess `json:"trustCenterAccess"`
|
TrustCenterAccess *TrustCenterAccess `json:"trustCenterAccess"`
|
||||||
Document *Document `json:"document,omitempty"`
|
Document *Document `json:"document,omitempty"`
|
||||||
Report *Report `json:"report,omitempty"`
|
ReportFile *File `json:"reportFile,omitempty"`
|
||||||
TrustCenterFile *TrustCenterFile `json:"trustCenterFile,omitempty"`
|
TrustCenterFile *TrustCenterFile `json:"trustCenterFile,omitempty"`
|
||||||
|
|
||||||
// Internal fields used by resolvers
|
// Internal fields used by resolvers
|
||||||
TrustCenterAccessID gid.GID `json:"-"`
|
TrustCenterAccessID gid.GID `json:"-"`
|
||||||
DocumentID *gid.GID `json:"-"`
|
DocumentID *gid.GID `json:"-"`
|
||||||
ReportID *gid.GID `json:"-"`
|
ReportFileID *gid.GID `json:"-"`
|
||||||
TrustCenterFileID *gid.GID `json:"-"`
|
TrustCenterFileID *gid.GID `json:"-"`
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -62,7 +62,7 @@ func NewTrustCenterDocumentAccess(tcda *coredata.TrustCenterDocumentAccess) *Tru
|
|||||||
UpdatedAt: tcda.UpdatedAt,
|
UpdatedAt: tcda.UpdatedAt,
|
||||||
TrustCenterAccessID: tcda.TrustCenterAccessID,
|
TrustCenterAccessID: tcda.TrustCenterAccessID,
|
||||||
DocumentID: tcda.DocumentID,
|
DocumentID: tcda.DocumentID,
|
||||||
ReportID: tcda.ReportID,
|
ReportFileID: tcda.ReportFileID,
|
||||||
TrustCenterFileID: tcda.TrustCenterFileID,
|
TrustCenterFileID: tcda.TrustCenterFileID,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,9 +72,9 @@ func NewTrustCenterDocumentAccess(tcda *coredata.TrustCenterDocumentAccess) *Tru
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if tcda.ReportID != nil {
|
if tcda.ReportFileID != nil {
|
||||||
object.Report = &Report{
|
object.ReportFile = &File{
|
||||||
ID: *tcda.ReportID,
|
ID: *tcda.ReportFileID,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1468,16 +1468,16 @@ func (r *Resolver) GetAuditTool(ctx context.Context, req *mcp.CallToolRequest, i
|
|||||||
return nil, types.GetAuditOutput{}, fmt.Errorf("cannot get audit: %w", err)
|
return nil, types.GetAuditOutput{}, fmt.Errorf("cannot get audit: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var report *coredata.Report
|
var file *coredata.File
|
||||||
if audit.ReportID != nil {
|
if audit.ReportFileID != nil {
|
||||||
report, err = prb.Reports.Get(ctx, scope, *audit.ReportID)
|
file, err = prb.Files.Get(ctx, scope, *audit.ReportFileID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, types.GetAuditOutput{}, fmt.Errorf("cannot get audit report: %w", err)
|
return nil, types.GetAuditOutput{}, fmt.Errorf("cannot get audit report file: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, types.GetAuditOutput{
|
return nil, types.GetAuditOutput{
|
||||||
Audit: types.NewAudit(audit, report),
|
Audit: types.NewAudit(audit, file),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1532,16 +1532,16 @@ func (r *Resolver) UpdateAuditTool(ctx context.Context, req *mcp.CallToolRequest
|
|||||||
return nil, types.UpdateAuditOutput{}, fmt.Errorf("cannot update audit: %w", err)
|
return nil, types.UpdateAuditOutput{}, fmt.Errorf("cannot update audit: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var report *coredata.Report
|
var file *coredata.File
|
||||||
if audit.ReportID != nil {
|
if audit.ReportFileID != nil {
|
||||||
report, err = svc.Reports.Get(ctx, scope, *audit.ReportID)
|
file, err = svc.Files.Get(ctx, scope, *audit.ReportFileID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, types.UpdateAuditOutput{}, fmt.Errorf("cannot get audit report: %w", err)
|
return nil, types.UpdateAuditOutput{}, fmt.Errorf("cannot get audit report file: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, types.UpdateAuditOutput{
|
return nil, types.UpdateAuditOutput{
|
||||||
Audit: types.NewAudit(audit, report),
|
Audit: types.NewAudit(audit, file),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import (
|
|||||||
"go.probo.inc/probo/pkg/page"
|
"go.probo.inc/probo/pkg/page"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewAudit(a *coredata.Audit, report *coredata.Report) *Audit {
|
func NewAudit(a *coredata.Audit, file *coredata.File) *Audit {
|
||||||
audit := &Audit{
|
audit := &Audit{
|
||||||
ID: a.ID,
|
ID: a.ID,
|
||||||
Name: a.Name,
|
Name: a.Name,
|
||||||
@@ -27,16 +27,16 @@ func NewAudit(a *coredata.Audit, report *coredata.Report) *Audit {
|
|||||||
FrameworkID: a.FrameworkID,
|
FrameworkID: a.FrameworkID,
|
||||||
State: a.State,
|
State: a.State,
|
||||||
TrustCenterVisibility: a.TrustCenterVisibility,
|
TrustCenterVisibility: a.TrustCenterVisibility,
|
||||||
HasReport: a.ReportID != nil,
|
HasReport: a.ReportFileID != nil,
|
||||||
ValidFrom: a.ValidFrom,
|
ValidFrom: a.ValidFrom,
|
||||||
ValidUntil: a.ValidUntil,
|
ValidUntil: a.ValidUntil,
|
||||||
CreatedAt: a.CreatedAt,
|
CreatedAt: a.CreatedAt,
|
||||||
UpdatedAt: a.UpdatedAt,
|
UpdatedAt: a.UpdatedAt,
|
||||||
}
|
}
|
||||||
|
|
||||||
if report != nil {
|
if file != nil {
|
||||||
audit.ReportFilename = &report.Filename
|
audit.ReportFilename = &file.FileName
|
||||||
audit.ReportMimeType = &report.MimeType
|
audit.ReportMimeType = &file.MimeType
|
||||||
}
|
}
|
||||||
|
|
||||||
return audit
|
return audit
|
||||||
|
|||||||
@@ -227,7 +227,7 @@ func SlackHandler(slackSvc *slack.Service, slackSigningSecret string, logger *lo
|
|||||||
switch gID.EntityType() {
|
switch gID.EntityType() {
|
||||||
case coredata.DocumentEntityType:
|
case coredata.DocumentEntityType:
|
||||||
documentIDs = []gid.GID{gID}
|
documentIDs = []gid.GID{gID}
|
||||||
case coredata.ReportEntityType:
|
case coredata.FileEntityType:
|
||||||
reportIDs = []gid.GID{gID}
|
reportIDs = []gid.GID{gID}
|
||||||
case coredata.TrustCenterFileEntityType:
|
case coredata.TrustCenterFileEntityType:
|
||||||
fileIDs = []gid.GID{gID}
|
fileIDs = []gid.GID{gID}
|
||||||
|
|||||||
@@ -83,21 +83,21 @@ func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error
|
|||||||
|
|
||||||
return types.NewFramework(framework), nil
|
return types.NewFramework(framework), nil
|
||||||
|
|
||||||
case coredata.ReportEntityType:
|
case coredata.FileEntityType:
|
||||||
trustCenter := compliancepage.CompliancePageFromContext(ctx)
|
trustCenter := compliancepage.CompliancePageFromContext(ctx)
|
||||||
|
|
||||||
report, err := trustService.Reports.Get(ctx, scope, trustCenter.OrganizationID, id)
|
file, err := trustService.Reports.Get(ctx, scope, trustCenter.OrganizationID, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, trust.ErrReportNotFound) || errors.Is(err, coredata.ErrResourceNotFound) {
|
if errors.Is(err, trust.ErrReportNotFound) || errors.Is(err, coredata.ErrResourceNotFound) {
|
||||||
return nil, gqlutils.NotFoundf(ctx, "node %q not found", id)
|
return nil, gqlutils.NotFoundf(ctx, "node %q not found", id)
|
||||||
}
|
}
|
||||||
|
|
||||||
r.logger.ErrorCtx(ctx, "cannot get report", log.Error(err))
|
r.logger.ErrorCtx(ctx, "cannot get audit report file", log.Error(err))
|
||||||
|
|
||||||
return nil, gqlutils.Internal(ctx)
|
return nil, gqlutils.Internal(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
return types.NewReport(report), nil
|
return types.NewAuditReport(file), nil
|
||||||
|
|
||||||
case coredata.AuditEntityType:
|
case coredata.AuditEntityType:
|
||||||
audit, err := trustService.Audits.Get(ctx, scope, id)
|
audit, err := trustService.Audits.Get(ctx, scope, id)
|
||||||
|
|||||||
@@ -114,9 +114,9 @@ type Framework implements Node @nda {
|
|||||||
darkLogoURL: String @goField(forceResolver: true)
|
darkLogoURL: String @goField(forceResolver: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Report implements Node @nda {
|
type AuditReport implements Node @nda {
|
||||||
id: ID!
|
id: ID!
|
||||||
filename: String!
|
fileName: String!
|
||||||
isUserAuthorized: Boolean! @goField(forceResolver: true)
|
isUserAuthorized: Boolean! @goField(forceResolver: true)
|
||||||
access: DocumentAccess @goField(forceResolver: true)
|
access: DocumentAccess @goField(forceResolver: true)
|
||||||
}
|
}
|
||||||
@@ -125,7 +125,7 @@ type Audit implements Node @nda {
|
|||||||
id: ID!
|
id: ID!
|
||||||
name: String
|
name: String
|
||||||
framework: Framework! @goField(forceResolver: true)
|
framework: Framework! @goField(forceResolver: true)
|
||||||
report: Report @goField(forceResolver: true)
|
reportFile: AuditReport @goField(forceResolver: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
type AuditConnection @nda {
|
type AuditConnection @nda {
|
||||||
|
|||||||
@@ -44,8 +44,8 @@ func (r *auditResolver) Framework(ctx context.Context, obj *types.Audit) (*types
|
|||||||
return types.NewFramework(framework), nil
|
return types.NewFramework(framework), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Report is the resolver for the report field.
|
// ReportFile is the resolver for the reportFile field.
|
||||||
func (r *auditResolver) Report(ctx context.Context, obj *types.Audit) (*types.Report, error) {
|
func (r *auditResolver) ReportFile(ctx context.Context, obj *types.Audit) (*types.AuditReport, error) {
|
||||||
scope := coredata.NewScopeFromObjectID(obj.ID)
|
scope := coredata.NewScopeFromObjectID(obj.ID)
|
||||||
trustService := r.trust
|
trustService := r.trust
|
||||||
|
|
||||||
@@ -55,19 +55,105 @@ func (r *auditResolver) Report(ctx context.Context, obj *types.Audit) (*types.Re
|
|||||||
return nil, gqlutils.Internal(ctx)
|
return nil, gqlutils.Internal(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
if audit.ReportID == nil {
|
if audit.ReportFileID == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
trustCenter := compliancepage.CompliancePageFromContext(ctx)
|
trustCenter := compliancepage.CompliancePageFromContext(ctx)
|
||||||
|
|
||||||
report, err := trustService.Reports.Get(ctx, scope, trustCenter.OrganizationID, *audit.ReportID)
|
file, err := trustService.Reports.Get(ctx, scope, trustCenter.OrganizationID, *audit.ReportFileID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.logger.ErrorCtx(ctx, "cannot load report", log.Error(err))
|
r.logger.ErrorCtx(ctx, "cannot load report file", log.Error(err))
|
||||||
return nil, gqlutils.Internal(ctx)
|
return nil, gqlutils.Internal(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
return types.NewReport(report), nil
|
return types.NewAuditReport(file), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsUserAuthorized is the resolver for the isUserAuthorized field.
|
||||||
|
func (r *auditReportResolver) IsUserAuthorized(ctx context.Context, obj *types.AuditReport) (bool, error) {
|
||||||
|
scope := coredata.NewScopeFromObjectID(obj.ID)
|
||||||
|
trustService := r.trust
|
||||||
|
trustCenter := compliancepage.CompliancePageFromContext(ctx)
|
||||||
|
|
||||||
|
audit, err := trustService.Audits.GetByReportFileID(ctx, scope, obj.ID)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, coredata.ErrResourceNotFound) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
r.logger.ErrorCtx(ctx, "cannot load audit for report file", log.Error(err))
|
||||||
|
|
||||||
|
return false, gqlutils.Internal(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
if audit.TrustCenterVisibility == coredata.TrustCenterVisibilityPublic {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
identity := authn.IdentityFromContext(ctx)
|
||||||
|
if identity == nil {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
reportAccess, err := trustService.TrustCenterAccesses.GetReportFileAccess(ctx, scope,
|
||||||
|
trustCenter.ID,
|
||||||
|
identity.ID,
|
||||||
|
obj.ID,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, trust.ErrMembershipNotFound) ||
|
||||||
|
errors.Is(err, trust.ErrUserNotFound) ||
|
||||||
|
errors.Is(err, trust.ErrUserInactive) ||
|
||||||
|
errors.Is(err, trust.ErrDocumentAccessNotFound) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
r.logger.ErrorCtx(ctx, "cannot check report access", log.Error(err))
|
||||||
|
|
||||||
|
return false, gqlutils.Internal(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
return reportAccess.Status == coredata.TrustCenterDocumentAccessStatusGranted, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Access is the resolver for the access field.
|
||||||
|
func (r *auditReportResolver) Access(ctx context.Context, obj *types.AuditReport) (*types.DocumentAccess, error) {
|
||||||
|
scope := coredata.NewScopeFromObjectID(obj.ID)
|
||||||
|
trustService := r.trust
|
||||||
|
trustCenter := compliancepage.CompliancePageFromContext(ctx)
|
||||||
|
|
||||||
|
identity := authn.IdentityFromContext(ctx)
|
||||||
|
if identity == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
access, err := trustService.TrustCenterAccesses.GetReportFileAccess(
|
||||||
|
ctx, scope,
|
||||||
|
trustCenter.ID,
|
||||||
|
identity.ID,
|
||||||
|
obj.ID,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, trust.ErrMembershipNotFound) ||
|
||||||
|
errors.Is(err, trust.ErrUserNotFound) ||
|
||||||
|
errors.Is(err, trust.ErrDocumentAccessNotFound) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if errors.Is(err, trust.ErrUserInactive) {
|
||||||
|
return nil, gqlutils.Forbidden(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
r.logger.ErrorCtx(ctx, "cannot get audit report access", log.Error(err))
|
||||||
|
|
||||||
|
return nil, gqlutils.Internal(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &types.DocumentAccess{
|
||||||
|
ID: access.ID,
|
||||||
|
Status: access.Status,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Framework is the resolver for the framework field on ComplianceFramework.
|
// Framework is the resolver for the framework field on ComplianceFramework.
|
||||||
@@ -294,7 +380,7 @@ func (r *mutationResolver) ExportReportPDF(ctx context.Context, input types.Expo
|
|||||||
trustService := r.trust
|
trustService := r.trust
|
||||||
trustCenter := compliancepage.CompliancePageFromContext(ctx)
|
trustCenter := compliancepage.CompliancePageFromContext(ctx)
|
||||||
|
|
||||||
audit, err := trustService.Audits.GetByReportID(ctx, scope, input.ReportID)
|
audit, err := trustService.Audits.GetByReportFileID(ctx, scope, input.ReportID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.logger.ErrorCtx(ctx, "cannot load audit", log.Error(err))
|
r.logger.ErrorCtx(ctx, "cannot load audit", log.Error(err))
|
||||||
return nil, gqlutils.Internal(ctx)
|
return nil, gqlutils.Internal(ctx)
|
||||||
@@ -317,7 +403,7 @@ func (r *mutationResolver) ExportReportPDF(ctx context.Context, input types.Expo
|
|||||||
return nil, gqlutils.Unauthenticatedf(ctx, "unauthenticated")
|
return nil, gqlutils.Unauthenticatedf(ctx, "unauthenticated")
|
||||||
}
|
}
|
||||||
|
|
||||||
reportAccess, err := trustService.TrustCenterAccesses.GetReportAccess(
|
reportAccess, err := trustService.TrustCenterAccesses.GetReportFileAccess(
|
||||||
ctx, scope,
|
ctx, scope,
|
||||||
trustCenter.ID,
|
trustCenter.ID,
|
||||||
identity.ID,
|
identity.ID,
|
||||||
@@ -458,7 +544,7 @@ func (r *mutationResolver) RequestReportAccess(ctx context.Context, input types.
|
|||||||
scope := coredata.NewScopeFromObjectID(trustCenter.ID)
|
scope := coredata.NewScopeFromObjectID(trustCenter.ID)
|
||||||
trustService := r.trust
|
trustService := r.trust
|
||||||
|
|
||||||
audit, err := trustService.Audits.GetByReportID(ctx, scope, input.ReportID)
|
audit, err := trustService.Audits.GetByReportFileID(ctx, scope, input.ReportID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.logger.ErrorCtx(ctx, "cannot load audit", log.Error(err))
|
r.logger.ErrorCtx(ctx, "cannot load audit", log.Error(err))
|
||||||
return nil, gqlutils.Internal(ctx)
|
return nil, gqlutils.Internal(ctx)
|
||||||
@@ -543,87 +629,6 @@ func (r *mutationResolver) RequestTrustCenterFileAccess(ctx context.Context, inp
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsUserAuthorized is the resolver for the isUserAuthorized field.
|
|
||||||
func (r *reportResolver) IsUserAuthorized(ctx context.Context, obj *types.Report) (bool, error) {
|
|
||||||
scope := coredata.NewScopeFromObjectID(obj.ID)
|
|
||||||
trustService := r.trust
|
|
||||||
trustCenter := compliancepage.CompliancePageFromContext(ctx)
|
|
||||||
|
|
||||||
audit, err := trustService.Audits.GetByReportID(ctx, scope, obj.ID)
|
|
||||||
if err != nil {
|
|
||||||
r.logger.ErrorCtx(ctx, "cannot load document", log.Error(err))
|
|
||||||
return false, gqlutils.Internal(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
if audit.TrustCenterVisibility == coredata.TrustCenterVisibilityPublic {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
identity := authn.IdentityFromContext(ctx)
|
|
||||||
if identity == nil {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
reportAccess, err := trustService.TrustCenterAccesses.GetReportAccess(ctx, scope,
|
|
||||||
trustCenter.ID,
|
|
||||||
identity.ID,
|
|
||||||
obj.ID,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(err, trust.ErrMembershipNotFound) ||
|
|
||||||
errors.Is(err, trust.ErrUserNotFound) ||
|
|
||||||
errors.Is(err, trust.ErrUserInactive) ||
|
|
||||||
errors.Is(err, trust.ErrDocumentAccessNotFound) {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
r.logger.ErrorCtx(ctx, "cannot check report access", log.Error(err))
|
|
||||||
|
|
||||||
return false, gqlutils.Internal(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
return reportAccess.Status == coredata.TrustCenterDocumentAccessStatusGranted, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Access is the resolver for the access field.
|
|
||||||
func (r *reportResolver) Access(ctx context.Context, obj *types.Report) (*types.DocumentAccess, error) {
|
|
||||||
scope := coredata.NewScopeFromObjectID(obj.ID)
|
|
||||||
trustService := r.trust
|
|
||||||
trustCenter := compliancepage.CompliancePageFromContext(ctx)
|
|
||||||
|
|
||||||
identity := authn.IdentityFromContext(ctx)
|
|
||||||
if identity == nil {
|
|
||||||
return nil, nil // User is not authenticated, so no access requested
|
|
||||||
}
|
|
||||||
|
|
||||||
access, err := trustService.TrustCenterAccesses.GetReportAccess(
|
|
||||||
ctx, scope,
|
|
||||||
trustCenter.ID,
|
|
||||||
identity.ID,
|
|
||||||
obj.ID,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(err, trust.ErrMembershipNotFound) ||
|
|
||||||
errors.Is(err, trust.ErrUserNotFound) ||
|
|
||||||
errors.Is(err, trust.ErrDocumentAccessNotFound) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if errors.Is(err, trust.ErrUserInactive) {
|
|
||||||
return nil, gqlutils.Forbidden(ctx, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
r.logger.ErrorCtx(ctx, "cannot get audit report access", log.Error(err))
|
|
||||||
|
|
||||||
return nil, gqlutils.Internal(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &types.DocumentAccess{
|
|
||||||
ID: access.ID,
|
|
||||||
Status: access.Status,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TotalCount is the resolver for the totalCount field.
|
// TotalCount is the resolver for the totalCount field.
|
||||||
func (r *subprocessorConnectionResolver) TotalCount(ctx context.Context, obj *types.SubprocessorConnection) (int, error) {
|
func (r *subprocessorConnectionResolver) TotalCount(ctx context.Context, obj *types.SubprocessorConnection) (int, error) {
|
||||||
scope := coredata.NewScopeFromObjectID(obj.ParentID)
|
scope := coredata.NewScopeFromObjectID(obj.ParentID)
|
||||||
@@ -987,6 +992,9 @@ func (r *trustCenterReferenceResolver) LogoURL(ctx context.Context, obj *types.T
|
|||||||
// Audit returns schema.AuditResolver implementation.
|
// Audit returns schema.AuditResolver implementation.
|
||||||
func (r *Resolver) Audit() schema.AuditResolver { return &auditResolver{r} }
|
func (r *Resolver) Audit() schema.AuditResolver { return &auditResolver{r} }
|
||||||
|
|
||||||
|
// AuditReport returns schema.AuditReportResolver implementation.
|
||||||
|
func (r *Resolver) AuditReport() schema.AuditReportResolver { return &auditReportResolver{r} }
|
||||||
|
|
||||||
// ComplianceFramework returns schema.ComplianceFrameworkResolver implementation.
|
// ComplianceFramework returns schema.ComplianceFrameworkResolver implementation.
|
||||||
func (r *Resolver) ComplianceFramework() schema.ComplianceFrameworkResolver {
|
func (r *Resolver) ComplianceFramework() schema.ComplianceFrameworkResolver {
|
||||||
return &complianceFrameworkResolver{r}
|
return &complianceFrameworkResolver{r}
|
||||||
@@ -998,9 +1006,6 @@ func (r *Resolver) Document() schema.DocumentResolver { return &documentResolver
|
|||||||
// Framework returns schema.FrameworkResolver implementation.
|
// Framework returns schema.FrameworkResolver implementation.
|
||||||
func (r *Resolver) Framework() schema.FrameworkResolver { return &frameworkResolver{r} }
|
func (r *Resolver) Framework() schema.FrameworkResolver { return &frameworkResolver{r} }
|
||||||
|
|
||||||
// Report returns schema.ReportResolver implementation.
|
|
||||||
func (r *Resolver) Report() schema.ReportResolver { return &reportResolver{r} }
|
|
||||||
|
|
||||||
// SubprocessorConnection returns schema.SubprocessorConnectionResolver implementation.
|
// SubprocessorConnection returns schema.SubprocessorConnectionResolver implementation.
|
||||||
func (r *Resolver) SubprocessorConnection() schema.SubprocessorConnectionResolver {
|
func (r *Resolver) SubprocessorConnection() schema.SubprocessorConnectionResolver {
|
||||||
return &subprocessorConnectionResolver{r}
|
return &subprocessorConnectionResolver{r}
|
||||||
@@ -1020,10 +1025,10 @@ func (r *Resolver) TrustCenterReference() schema.TrustCenterReferenceResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type auditResolver struct{ *Resolver }
|
type auditResolver struct{ *Resolver }
|
||||||
|
type auditReportResolver struct{ *Resolver }
|
||||||
type complianceFrameworkResolver struct{ *Resolver }
|
type complianceFrameworkResolver struct{ *Resolver }
|
||||||
type documentResolver struct{ *Resolver }
|
type documentResolver struct{ *Resolver }
|
||||||
type frameworkResolver struct{ *Resolver }
|
type frameworkResolver struct{ *Resolver }
|
||||||
type reportResolver struct{ *Resolver }
|
|
||||||
type subprocessorConnectionResolver struct{ *Resolver }
|
type subprocessorConnectionResolver struct{ *Resolver }
|
||||||
type trustCenterResolver struct{ *Resolver }
|
type trustCenterResolver struct{ *Resolver }
|
||||||
type trustCenterFileResolver struct{ *Resolver }
|
type trustCenterFileResolver struct{ *Resolver }
|
||||||
|
|||||||
@@ -18,9 +18,9 @@ import (
|
|||||||
"go.probo.inc/probo/pkg/coredata"
|
"go.probo.inc/probo/pkg/coredata"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewReport(r *coredata.Report) *Report {
|
func NewAuditReport(f *coredata.File) *AuditReport {
|
||||||
return &Report{
|
return &AuditReport{
|
||||||
ID: r.ID,
|
ID: f.ID,
|
||||||
Filename: r.Filename,
|
FileName: f.FileName,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -349,14 +349,9 @@ func (s *Service) loadDocumentsReportsAndFilesFromAccesses(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if access.ReportID != nil {
|
if access.ReportFileID != nil {
|
||||||
rep := &coredata.Report{}
|
|
||||||
if err := rep.LoadByID(ctx, conn, scope, *access.ReportID); err != nil {
|
|
||||||
return nil, nil, nil, fmt.Errorf("cannot load report: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
audit := &coredata.Audit{}
|
audit := &coredata.Audit{}
|
||||||
if err := audit.LoadByReportID(ctx, conn, scope, *access.ReportID); err != nil {
|
if err := audit.LoadByReportFileID(ctx, conn, scope, *access.ReportFileID); err != nil {
|
||||||
return nil, nil, nil, fmt.Errorf("cannot load audit: %w", err)
|
return nil, nil, nil, fmt.Errorf("cannot load audit: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -373,7 +368,7 @@ func (s *Service) loadDocumentsReportsAndFilesFromAccesses(
|
|||||||
reports = append(
|
reports = append(
|
||||||
reports,
|
reports,
|
||||||
SlackMessageReport{
|
SlackMessageReport{
|
||||||
ID: access.ReportID.String(),
|
ID: access.ReportFileID.String(),
|
||||||
Title: label,
|
Title: label,
|
||||||
AuditID: audit.ID.String(),
|
AuditID: audit.ID.String(),
|
||||||
Status: access.Status.String(),
|
Status: access.Status.String(),
|
||||||
|
|||||||
@@ -53,18 +53,17 @@ func (s AuditService) Get(
|
|||||||
return audit, nil
|
return audit, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s AuditService) GetByReportID(
|
func (s AuditService) GetByReportFileID(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
scope coredata.Scoper,
|
scope coredata.Scoper,
|
||||||
reportID gid.GID,
|
fileID gid.GID,
|
||||||
) (*coredata.Audit, error) {
|
) (*coredata.Audit, error) {
|
||||||
audit := &coredata.Audit{}
|
audit := &coredata.Audit{}
|
||||||
|
|
||||||
err := s.svc.pg.WithConn(
|
err := s.svc.pg.WithConn(
|
||||||
ctx,
|
ctx,
|
||||||
func(ctx context.Context, conn pg.Querier) error {
|
func(ctx context.Context, conn pg.Querier) error {
|
||||||
err := audit.LoadByReportID(ctx, conn, scope, reportID)
|
if err := audit.LoadByReportFileID(ctx, conn, scope, fileID); err != nil {
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("cannot load audit: %w", err)
|
return fmt.Errorf("cannot load audit: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ package trust
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"time"
|
"time"
|
||||||
@@ -36,33 +37,43 @@ func (s ReportService) Get(
|
|||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
scope coredata.Scoper,
|
scope coredata.Scoper,
|
||||||
organizationID gid.GID,
|
organizationID gid.GID,
|
||||||
reportID gid.GID,
|
fileID gid.GID,
|
||||||
) (*coredata.Report, error) {
|
) (*coredata.File, error) {
|
||||||
report, err := s.loadByID(ctx, scope, reportID)
|
file, err := s.loadByID(ctx, scope, fileID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if report.OrganizationID != organizationID {
|
if file.OrganizationID != organizationID {
|
||||||
return nil, ErrReportNotFound
|
return nil, ErrReportNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
return report, nil
|
// check the given report file ID is linked to an audit in order to avoid
|
||||||
|
// being able to get any file from the report request.
|
||||||
|
_, err = s.svc.Audits.GetByReportFileID(ctx, scope, fileID)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, coredata.ErrResourceNotFound) {
|
||||||
|
return nil, ErrReportNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("cannot verify report file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return file, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s ReportService) loadByID(
|
func (s ReportService) loadByID(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
scope coredata.Scoper,
|
scope coredata.Scoper,
|
||||||
reportID gid.GID,
|
fileID gid.GID,
|
||||||
) (*coredata.Report, error) {
|
) (*coredata.File, error) {
|
||||||
report := &coredata.Report{}
|
file := &coredata.File{}
|
||||||
|
|
||||||
err := s.svc.pg.WithConn(
|
err := s.svc.pg.WithConn(
|
||||||
ctx,
|
ctx,
|
||||||
func(ctx context.Context, conn pg.Querier) error {
|
func(ctx context.Context, conn pg.Querier) error {
|
||||||
err := report.LoadByID(ctx, conn, scope, reportID)
|
if err := file.LoadActiveByID(ctx, conn, scope, fileID); err != nil {
|
||||||
if err != nil {
|
return fmt.Errorf("cannot load file: %w", err)
|
||||||
return fmt.Errorf("cannot load report: %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -72,28 +83,28 @@ func (s ReportService) loadByID(
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return report, nil
|
return file, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s ReportService) GenerateDownloadURL(
|
func (s ReportService) GenerateDownloadURL(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
scope coredata.Scoper,
|
scope coredata.Scoper,
|
||||||
reportID gid.GID,
|
fileID gid.GID,
|
||||||
expiresIn time.Duration,
|
expiresIn time.Duration,
|
||||||
) (*string, error) {
|
) (*string, error) {
|
||||||
report, err := s.loadByID(ctx, scope, reportID)
|
file, err := s.loadByID(ctx, scope, fileID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot get report: %w", err)
|
return nil, fmt.Errorf("cannot get file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
presignClient := s3.NewPresignClient(s.svc.s3)
|
presignClient := s3.NewPresignClient(s.svc.s3)
|
||||||
|
|
||||||
presignedReq, err := presignClient.PresignGetObject(ctx, &s3.GetObjectInput{
|
presignedReq, err := presignClient.PresignGetObject(ctx, &s3.GetObjectInput{
|
||||||
Bucket: new(s.svc.bucket),
|
Bucket: new(s.svc.bucket),
|
||||||
Key: new(report.ObjectKey),
|
Key: new(file.FileKey),
|
||||||
ResponseCacheControl: new("max-age=3600, public"),
|
ResponseCacheControl: new("max-age=3600, public"),
|
||||||
ResponseContentType: new(report.MimeType),
|
ResponseContentType: new(file.MimeType),
|
||||||
ResponseContentDisposition: new(fmt.Sprintf("attachment; filename=\"%s\"", report.Filename)),
|
ResponseContentDisposition: new(fmt.Sprintf("attachment; filename=\"%s\"", file.FileName)),
|
||||||
}, func(opts *s3.PresignOptions) {
|
}, func(opts *s3.PresignOptions) {
|
||||||
opts.Expires = expiresIn
|
opts.Expires = expiresIn
|
||||||
})
|
})
|
||||||
@@ -134,16 +145,16 @@ func (s ReportService) ExportPDFWithoutWatermark(
|
|||||||
func (s ReportService) exportPDFData(
|
func (s ReportService) exportPDFData(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
scope coredata.Scoper,
|
scope coredata.Scoper,
|
||||||
reportID gid.GID,
|
fileID gid.GID,
|
||||||
) ([]byte, error) {
|
) ([]byte, error) {
|
||||||
report, err := s.loadByID(ctx, scope, reportID)
|
file, err := s.loadByID(ctx, scope, fileID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot get report: %w", err)
|
return nil, fmt.Errorf("cannot get file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := s.svc.s3.GetObject(ctx, &s3.GetObjectInput{
|
result, err := s.svc.s3.GetObject(ctx, &s3.GetObjectInput{
|
||||||
Bucket: new(s.svc.bucket),
|
Bucket: new(s.svc.bucket),
|
||||||
Key: new(report.ObjectKey),
|
Key: new(file.FileKey),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot download PDF from S3: %w", err)
|
return nil, fmt.Errorf("cannot download PDF from S3: %w", err)
|
||||||
|
|||||||
@@ -100,8 +100,8 @@ func (s TrustCenterAccessService) Request(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, audit := range allAudits {
|
for _, audit := range allAudits {
|
||||||
if audit.ReportID != nil {
|
if audit.ReportFileID != nil {
|
||||||
reportIDs = append(reportIDs, *audit.ReportID)
|
reportIDs = append(reportIDs, *audit.ReportFileID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -148,7 +148,7 @@ func (s TrustCenterAccessService) Request(
|
|||||||
return fmt.Errorf("cannot bulk insert trust center document accesses: %w", err)
|
return fmt.Errorf("cannot bulk insert trust center document accesses: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := accesses.BulkInsertReportAccesses(
|
if err := accesses.BulkInsertReportFileAccesses(
|
||||||
ctx,
|
ctx,
|
||||||
tx,
|
tx,
|
||||||
scope,
|
scope,
|
||||||
@@ -255,12 +255,12 @@ func (s TrustCenterAccessService) GetDocumentAccess(
|
|||||||
return documentAccess, nil
|
return documentAccess, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s TrustCenterAccessService) GetReportAccess(
|
func (s TrustCenterAccessService) GetReportFileAccess(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
scope coredata.Scoper,
|
scope coredata.Scoper,
|
||||||
trustCenterID gid.GID,
|
trustCenterID gid.GID,
|
||||||
identityID gid.GID,
|
identityID gid.GID,
|
||||||
reportID gid.GID,
|
reportFileID gid.GID,
|
||||||
) (*coredata.TrustCenterDocumentAccess, error) {
|
) (*coredata.TrustCenterDocumentAccess, error) {
|
||||||
var reportAccess *coredata.TrustCenterDocumentAccess
|
var reportAccess *coredata.TrustCenterDocumentAccess
|
||||||
|
|
||||||
@@ -289,7 +289,7 @@ func (s TrustCenterAccessService) GetReportAccess(
|
|||||||
|
|
||||||
reportAccess = &coredata.TrustCenterDocumentAccess{}
|
reportAccess = &coredata.TrustCenterDocumentAccess{}
|
||||||
|
|
||||||
err = reportAccess.LoadByTrustCenterAccessIDAndReportID(ctx, conn, scope, access.ID, reportID)
|
err = reportAccess.LoadByTrustCenterAccessIDAndReportFileID(ctx, conn, scope, access.ID, reportFileID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, coredata.ErrResourceNotFound) {
|
if errors.Is(err, coredata.ErrResourceNotFound) {
|
||||||
return ErrDocumentAccessNotFound
|
return ErrDocumentAccessNotFound
|
||||||
@@ -405,7 +405,7 @@ func (s *TrustCenterAccessService) GrantByIDs(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(reportIDs) > 0 {
|
if len(reportIDs) > 0 {
|
||||||
if err := coredata.GrantByReportIDs(ctx, tx, scope, access.ID, reportIDs, now); err != nil {
|
if err := coredata.GrantByReportFileIDs(ctx, tx, scope, access.ID, reportIDs, now); err != nil {
|
||||||
return fmt.Errorf("cannot grant report accesses: %w", err)
|
return fmt.Errorf("cannot grant report accesses: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -526,7 +526,7 @@ func (s *TrustCenterAccessService) RejectOrRevokeByIDs(
|
|||||||
if len(reportIDs) > 0 {
|
if len(reportIDs) > 0 {
|
||||||
shouldSendEmail = true
|
shouldSendEmail = true
|
||||||
|
|
||||||
if err := coredata.RejectOrRevokeByReportIDs(ctx, tx, scope, access.ID, reportIDs, now); err != nil {
|
if err := coredata.RejectOrRevokeByReportFileIDs(ctx, tx, scope, access.ID, reportIDs, now); err != nil {
|
||||||
return fmt.Errorf("cannot reject/revoke report accesses: %w", err)
|
return fmt.Errorf("cannot reject/revoke report accesses: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -645,8 +645,8 @@ func extractExistingIDs(accesses coredata.TrustCenterDocumentAccesses) ([]gid.GI
|
|||||||
documentIDs = append(documentIDs, *access.DocumentID)
|
documentIDs = append(documentIDs, *access.DocumentID)
|
||||||
}
|
}
|
||||||
|
|
||||||
if access.ReportID != nil {
|
if access.ReportFileID != nil {
|
||||||
reportIDs = append(reportIDs, *access.ReportID)
|
reportIDs = append(reportIDs, *access.ReportFileID)
|
||||||
}
|
}
|
||||||
|
|
||||||
if access.TrustCenterFileID != nil {
|
if access.TrustCenterFileID != nil {
|
||||||
@@ -678,36 +678,36 @@ func reportAccessLabels(
|
|||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
conn pg.Querier,
|
conn pg.Querier,
|
||||||
scope coredata.Scoper,
|
scope coredata.Scoper,
|
||||||
reportIDs []gid.GID,
|
reportFileIDs []gid.GID,
|
||||||
) ([]string, error) {
|
) ([]string, error) {
|
||||||
var reports coredata.Reports
|
var reportFiles coredata.Files
|
||||||
if err := reports.LoadByIDs(ctx, conn, scope, reportIDs); err != nil {
|
if err := reportFiles.LoadByIDs(ctx, conn, scope, reportFileIDs); err != nil {
|
||||||
return nil, fmt.Errorf("cannot load reports by IDs: %w", err)
|
return nil, fmt.Errorf("cannot load report files by IDs: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
reportByID := make(map[gid.GID]*coredata.Report, len(reports))
|
fileByID := make(map[gid.GID]*coredata.File, len(reportFiles))
|
||||||
for _, report := range reports {
|
for _, f := range reportFiles {
|
||||||
reportByID[report.ID] = report
|
fileByID[f.ID] = f
|
||||||
}
|
}
|
||||||
|
|
||||||
var audits coredata.Audits
|
var audits coredata.Audits
|
||||||
if err := audits.LoadByReportIDs(ctx, conn, scope, reportIDs); err != nil {
|
if err := audits.LoadByReportFileIDs(ctx, conn, scope, reportFileIDs); err != nil {
|
||||||
return nil, fmt.Errorf("cannot load audits by report IDs: %w", err)
|
return nil, fmt.Errorf("cannot load audits by report file IDs: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
auditByReportID := make(map[gid.GID]*coredata.Audit, len(audits))
|
auditByFileID := make(map[gid.GID]*coredata.Audit, len(audits))
|
||||||
frameworkIDSet := make(map[gid.GID]struct{})
|
frameworkIDSet := make(map[gid.GID]struct{})
|
||||||
|
|
||||||
for _, audit := range audits {
|
for _, audit := range audits {
|
||||||
if audit.ReportID == nil {
|
if audit.ReportFileID == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, exists := auditByReportID[*audit.ReportID]; exists {
|
if _, exists := auditByFileID[*audit.ReportFileID]; exists {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
auditByReportID[*audit.ReportID] = audit
|
auditByFileID[*audit.ReportFileID] = audit
|
||||||
frameworkIDSet[audit.FrameworkID] = struct{}{}
|
frameworkIDSet[audit.FrameworkID] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -729,23 +729,23 @@ func reportAccessLabels(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
labels := make([]string, 0, len(reportIDs))
|
labels := make([]string, 0, len(reportFileIDs))
|
||||||
|
|
||||||
for _, reportID := range reportIDs {
|
for _, fileID := range reportFileIDs {
|
||||||
report, ok := reportByID[reportID]
|
file, ok := fileByID[fileID]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("cannot load report %q: %w", reportID, coredata.ErrResourceNotFound)
|
return nil, fmt.Errorf("cannot load report file %q: %w", fileID, coredata.ErrResourceNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
audit, ok := auditByReportID[reportID]
|
audit, ok := auditByFileID[fileID]
|
||||||
if !ok {
|
if !ok {
|
||||||
labels = append(labels, report.Filename)
|
labels = append(labels, file.FileName)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
framework, ok := frameworkByID[audit.FrameworkID]
|
framework, ok := frameworkByID[audit.FrameworkID]
|
||||||
if !ok {
|
if !ok {
|
||||||
labels = append(labels, report.Filename)
|
labels = append(labels, file.FileName)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user