Add role management

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
Sacha Al Himdani
2025-11-07 09:20:38 +01:00
parent 696adb7d79
commit 21c4b7cd9d
143 changed files with 5976 additions and 2094 deletions

View File

@@ -47,13 +47,14 @@ func (a *UserAPIKeyMembership) Insert(
) error {
q := `
INSERT INTO
authz_api_keys_memberships (id, tenant_id, auth_user_api_key_id, membership_id, role, created_at, updated_at)
authz_api_keys_memberships (id, tenant_id, auth_user_api_key_id, membership_id, role, organization_id, created_at, updated_at)
VALUES (
@id,
@tenant_id,
@auth_user_api_key_id,
@membership_id,
@role,
@organization_id,
@created_at,
@updated_at
)
@@ -65,6 +66,7 @@ VALUES (
"auth_user_api_key_id": a.UserAPIKeyID,
"membership_id": a.MembershipID,
"role": a.Role,
"organization_id": a.OrganizationID,
"created_at": a.CreatedAt,
"updated_at": a.UpdatedAt,
}
@@ -127,6 +129,133 @@ ORDER BY akm.created_at DESC
return nil
}
// LoadRoleByAPIKeyAndEntityID loads an API key's role by querying any entity to extract its organization_id
func (a *UserAPIKeyMembership) LoadRoleByAPIKeyAndEntityID(
ctx context.Context,
conn pg.Conn,
scope Scoper,
apiKeyID gid.GID,
entityID gid.GID,
) error {
entityType := entityID.EntityType()
// For organization, the entity ID is the organization ID
if entityType == OrganizationEntityType {
return a.LoadByAPIKeyIDAndOrganizationID(ctx, conn, scope, apiKeyID, entityID)
}
tableName, ok := EntityTable(entityType)
if !ok {
return fmt.Errorf("unsupported entity type for API key role lookup: %d", entityType)
}
query := fmt.Sprintf(`
SELECT
akm.id,
akm.auth_user_api_key_id,
akm.membership_id,
akm.role,
akm.created_at,
akm.updated_at
FROM
authz_api_keys_memberships akm
INNER JOIN authz_memberships m ON m.id = akm.membership_id
INNER JOIN %s e ON e.id = @entity_id
WHERE
%s
AND akm.auth_user_api_key_id = @api_key_id
AND m.organization_id = e.organization_id
LIMIT 1;
`, tableName, scope.SQLFragment())
args := pgx.NamedArgs{
"api_key_id": apiKeyID,
"entity_id": entityID,
}
maps.Copy(args, scope.SQLArguments())
rows, err := conn.Query(ctx, query, args)
if err != nil {
return fmt.Errorf("cannot query API key membership by entity: %w", err)
}
defer rows.Close()
if !rows.Next() {
return fmt.Errorf("API key membership not found for key %s and entity %s", apiKeyID, entityID)
}
var membership UserAPIKeyMembership
err = rows.Scan(
&membership.ID,
&membership.UserAPIKeyID,
&membership.MembershipID,
&membership.Role,
&membership.CreatedAt,
&membership.UpdatedAt,
)
if err != nil {
return fmt.Errorf("cannot scan API key membership: %w", err)
}
*a = membership
return nil
}
func (a *UserAPIKeyMembership) LoadByAPIKeyIDAndOrganizationID(
ctx context.Context,
conn pg.Conn,
scope Scoper,
apiKeyID gid.GID,
organizationID gid.GID,
) error {
q := `
SELECT
akm.id,
akm.auth_user_api_key_id,
akm.membership_id,
akm.role,
akm.created_at,
akm.updated_at,
m.organization_id,
o.name as organization_name
FROM
authz_api_keys_memberships akm
JOIN
authz_memberships m ON akm.membership_id = m.id
JOIN
organizations o ON m.organization_id = o.id
WHERE
akm.auth_user_api_key_id = @api_key_id
AND m.organization_id = @organization_id
AND m.%s
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{
"api_key_id": apiKeyID,
"organization_id": organizationID,
}
maps.Copy(args, scope.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query user api key membership: %w", err)
}
membership, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[UserAPIKeyMembership])
if err != nil {
if err == pgx.ErrNoRows {
return fmt.Errorf("API key does not have access to organization")
}
return fmt.Errorf("cannot collect user api key membership: %w", err)
}
*a = membership
return nil
}
func (a *UserAPIKeyMembership) Delete(
ctx context.Context,
conn pg.Conn,
@@ -155,6 +284,56 @@ WHERE
return nil
}
func (a *UserAPIKeyMemberships) LoadByMembershipID(
ctx context.Context,
conn pg.Conn,
scope Scoper,
membershipID gid.GID,
) error {
q := `
SELECT
akm.id,
akm.auth_user_api_key_id,
akm.membership_id,
akm.role,
akm.created_at,
akm.updated_at,
m.organization_id,
o.name as organization_name
FROM
authz_api_keys_memberships akm
JOIN
authz_memberships m ON akm.membership_id = m.id
JOIN
organizations o ON m.organization_id = o.id
WHERE
akm.membership_id = @membership_id
AND m.%s
ORDER BY akm.created_at DESC
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{
"membership_id": membershipID,
}
maps.Copy(args, scope.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query user api key memberships by membership id: %w", err)
}
memberships, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[UserAPIKeyMembership])
if err != nil {
return fmt.Errorf("cannot collect user api key memberships: %w", err)
}
*a = memberships
return nil
}
func DeleteAllUserAPIKeyMembershipsByUserAPIKeyID(
ctx context.Context,
conn pg.Conn,

View File

@@ -46,6 +46,7 @@ func (av AssetVendors) Merge(
conn pg.Conn,
scope Scoper,
assetID gid.GID,
organizationID gid.GID,
vendorIDs []gid.GID,
) error {
q := `
@@ -54,6 +55,7 @@ WITH vendor_ids AS (
unnest(@vendor_ids::text[]) AS vendor_id,
@tenant_id AS tenant_id,
@asset_id AS asset_id,
@organization_id AS organization_id,
@created_at::timestamptz AS created_at
)
MERGE INTO asset_vendors AS tgt
@@ -62,18 +64,19 @@ ON tgt.tenant_id = src.tenant_id
AND tgt.asset_id = src.asset_id
AND tgt.vendor_id = src.vendor_id
WHEN NOT MATCHED
THEN INSERT (tenant_id, asset_id, vendor_id, created_at)
VALUES (src.tenant_id, src.asset_id, src.vendor_id, src.created_at)
THEN INSERT (tenant_id, asset_id, vendor_id, organization_id, created_at)
VALUES (src.tenant_id, src.asset_id, src.vendor_id, src.organization_id, src.created_at)
WHEN NOT MATCHED BY SOURCE
AND tgt.tenant_id = @tenant_id AND tgt.asset_id = @asset_id
THEN DELETE
`
args := pgx.StrictNamedArgs{
"tenant_id": scope.GetTenantID(),
"asset_id": assetID,
"created_at": time.Now(),
"vendor_ids": vendorIDs,
"tenant_id": scope.GetTenantID(),
"asset_id": assetID,
"organization_id": organizationID,
"created_at": time.Now(),
"vendor_ids": vendorIDs,
}
_, err := conn.Exec(ctx, q, args)
@@ -89,26 +92,29 @@ func (av AssetVendors) Insert(
conn pg.Conn,
scope Scoper,
assetID gid.GID,
organizationID gid.GID,
vendorIDs []gid.GID,
) error {
q := `
WITH vendor_ids AS (
SELECT unnest(@vendor_ids::text[]) AS vendor_id
)
INSERT INTO asset_vendors (tenant_id, asset_id, vendor_id, created_at)
INSERT INTO asset_vendors (tenant_id, asset_id, vendor_id, organization_id, created_at)
SELECT
@tenant_id AS tenant_id,
@asset_id AS asset_id,
vendor_id,
@organization_id AS organization_id,
@created_at AS created_at
FROM vendor_ids
`
args := pgx.StrictNamedArgs{
"tenant_id": scope.GetTenantID(),
"asset_id": assetID,
"created_at": time.Now(),
"vendor_ids": vendorIDs,
"tenant_id": scope.GetTenantID(),
"asset_id": assetID,
"organization_id": organizationID,
"created_at": time.Now(),
"vendor_ids": vendorIDs,
}
_, err := conn.Exec(ctx, q, args)

View File

@@ -31,6 +31,7 @@ import (
type (
Control struct {
ID gid.GID `db:"id"`
OrganizationID gid.GID `db:"organization_id"`
SectionTitle string `db:"section_title"`
FrameworkID gid.GID `db:"framework_id"`
Name string `db:"name"`
@@ -127,6 +128,7 @@ WITH ctrl AS (
c.id,
c.section_title,
c.framework_id,
c.organization_id,
c.tenant_id,
c.name,
c.description,
@@ -146,6 +148,7 @@ SELECT
id,
section_title,
framework_id,
organization_id,
name,
description,
status,
@@ -236,6 +239,7 @@ WITH ctrl AS (
c.id,
c.section_title,
c.framework_id,
c.organization_id,
c.tenant_id,
c.name,
c.description,
@@ -255,6 +259,7 @@ SELECT
id,
section_title,
framework_id,
organization_id,
name,
description,
status,
@@ -351,6 +356,7 @@ WITH ctrl AS (
c.id,
c.section_title,
c.framework_id,
c.organization_id,
c.tenant_id,
c.name,
c.description,
@@ -376,6 +382,7 @@ SELECT
id,
section_title,
framework_id,
organization_id,
name,
description,
status,
@@ -455,6 +462,7 @@ SELECT
id,
section_title,
framework_id,
organization_id,
name,
description,
status,
@@ -548,6 +556,7 @@ WITH ctrl AS (
c.id,
c.section_title,
c.framework_id,
c.organization_id,
c.tenant_id,
c.name,
c.description,
@@ -567,6 +576,7 @@ SELECT
id,
section_title,
framework_id,
organization_id,
name,
description,
status,
@@ -613,6 +623,7 @@ SELECT
id,
section_title,
framework_id,
organization_id,
name,
description,
status,
@@ -661,6 +672,7 @@ SELECT
id,
section_title,
framework_id,
organization_id,
name,
description,
status,
@@ -707,6 +719,7 @@ INSERT INTO
controls (
tenant_id,
id,
organization_id,
framework_id,
section_title,
name,
@@ -719,6 +732,7 @@ INSERT INTO
VALUES (
@tenant_id,
@control_id,
@organization_id,
@framework_id,
@section_title,
@name,
@@ -733,6 +747,7 @@ VALUES (
args := pgx.StrictNamedArgs{
"tenant_id": scope.GetTenantID(),
"control_id": c.ID,
"organization_id": c.OrganizationID,
"framework_id": c.FrameworkID,
"section_title": c.SectionTitle,
"name": c.Name,
@@ -884,6 +899,7 @@ WITH ctrl AS (
c.id,
c.section_title,
c.framework_id,
c.organization_id,
c.tenant_id,
c.name,
c.description,
@@ -903,6 +919,7 @@ SELECT
id,
section_title,
framework_id,
organization_id,
name,
description,
status,
@@ -994,6 +1011,7 @@ WITH ctrl AS (
c.id,
c.section_title,
c.framework_id,
c.organization_id,
c.tenant_id,
c.name,
c.description,
@@ -1013,6 +1031,7 @@ SELECT
id,
section_title,
framework_id,
organization_id,
name,
description,
status,

View File

@@ -27,9 +27,10 @@ import (
type (
ControlAudit struct {
ControlID gid.GID `db:"control_id"`
AuditID gid.GID `db:"audit_id"`
CreatedAt time.Time `db:"created_at"`
ControlID gid.GID `db:"control_id"`
AuditID gid.GID `db:"audit_id"`
OrganizationID gid.GID `db:"organization_id"`
CreatedAt time.Time `db:"created_at"`
}
ControlAudits []*ControlAudit
@@ -45,12 +46,14 @@ INSERT INTO
controls_audits (
control_id,
audit_id,
organization_id,
tenant_id,
created_at
)
VALUES (
@control_id,
@audit_id,
@organization_id,
@tenant_id,
@created_at
)
@@ -58,10 +61,11 @@ ON CONFLICT (control_id, audit_id) DO NOTHING;
`
args := pgx.StrictNamedArgs{
"control_id": ca.ControlID,
"audit_id": ca.AuditID,
"tenant_id": scope.GetTenantID(),
"created_at": ca.CreatedAt,
"control_id": ca.ControlID,
"audit_id": ca.AuditID,
"organization_id": ca.OrganizationID,
"tenant_id": scope.GetTenantID(),
"created_at": ca.CreatedAt,
}
_, err := conn.Exec(ctx, q, args)
return err

View File

@@ -29,10 +29,11 @@ import (
type (
ControlDocument struct {
ControlID gid.GID `db:"control_id"`
DocumentID gid.GID `db:"document_id"`
TenantID gid.TenantID `db:"tenant_id"`
CreatedAt time.Time `db:"created_at"`
ControlID gid.GID `db:"control_id"`
DocumentID gid.GID `db:"document_id"`
OrganizationID gid.GID `db:"organization_id"`
TenantID gid.TenantID `db:"tenant_id"`
CreatedAt time.Time `db:"created_at"`
}
ControlDocuments []*ControlDocument
@@ -57,22 +58,25 @@ INSERT INTO
controls_documents (
control_id,
document_id,
organization_id,
tenant_id,
created_at
)
VALUES (
@control_id,
@document_id,
@organization_id,
@tenant_id,
@created_at
);
`
args := pgx.StrictNamedArgs{
"control_id": cp.ControlID,
"document_id": cp.DocumentID,
"tenant_id": scope.GetTenantID(),
"created_at": cp.CreatedAt,
"control_id": cp.ControlID,
"document_id": cp.DocumentID,
"organization_id": cp.OrganizationID,
"tenant_id": scope.GetTenantID(),
"created_at": cp.CreatedAt,
}
_, err := conn.Exec(ctx, q, args)

View File

@@ -20,17 +20,18 @@ import (
"maps"
"time"
"go.probo.inc/probo/pkg/gid"
"github.com/jackc/pgx/v5"
"go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/gid"
)
type (
ControlMeasure struct {
ControlID gid.GID `db:"control_id"`
MeasureID gid.GID `db:"measure_id"`
TenantID gid.TenantID `db:"tenant_id"`
CreatedAt time.Time `db:"created_at"`
ControlID gid.GID `db:"control_id"`
MeasureID gid.GID `db:"measure_id"`
OrganizationID gid.GID `db:"organization_id"`
TenantID gid.TenantID `db:"tenant_id"`
CreatedAt time.Time `db:"created_at"`
}
ControlMeasures []*ControlMeasure
@@ -46,12 +47,14 @@ INSERT INTO
controls_measures (
control_id,
measure_id,
organization_id,
tenant_id,
created_at
)
VALUES (
@control_id,
@measure_id,
@organization_id,
@tenant_id,
@created_at
)
@@ -59,10 +62,11 @@ ON CONFLICT (control_id, measure_id) DO NOTHING;
`
args := pgx.StrictNamedArgs{
"control_id": cm.ControlID,
"measure_id": cm.MeasureID,
"tenant_id": scope.GetTenantID(),
"created_at": cm.CreatedAt,
"control_id": cm.ControlID,
"measure_id": cm.MeasureID,
"organization_id": cm.OrganizationID,
"tenant_id": scope.GetTenantID(),
"created_at": cm.CreatedAt,
}
_, err := conn.Exec(ctx, q, args)
return err

View File

@@ -27,9 +27,10 @@ import (
type (
ControlSnapshot struct {
ControlID gid.GID `db:"control_id"`
SnapshotID gid.GID `db:"snapshot_id"`
CreatedAt time.Time `db:"created_at"`
ControlID gid.GID `db:"control_id"`
SnapshotID gid.GID `db:"snapshot_id"`
OrganizationID gid.GID `db:"organization_id"`
CreatedAt time.Time `db:"created_at"`
}
ControlSnapshots []*ControlSnapshot
@@ -45,12 +46,14 @@ INSERT INTO
controls_snapshots (
control_id,
snapshot_id,
organization_id,
tenant_id,
created_at
)
VALUES (
@control_id,
@snapshot_id,
@organization_id,
@tenant_id,
@created_at
)
@@ -58,10 +61,11 @@ ON CONFLICT (control_id, snapshot_id) DO NOTHING;
`
args := pgx.StrictNamedArgs{
"control_id": cs.ControlID,
"snapshot_id": cs.SnapshotID,
"tenant_id": scope.GetTenantID(),
"created_at": cs.CreatedAt,
"control_id": cs.ControlID,
"snapshot_id": cs.SnapshotID,
"organization_id": cs.OrganizationID,
"tenant_id": scope.GetTenantID(),
"created_at": cs.CreatedAt,
}
_, err := conn.Exec(ctx, q, args)
return err

View File

@@ -22,17 +22,18 @@ import (
"maps"
"time"
"go.probo.inc/probo/pkg/crypto/cipher"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/page"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgconn"
"go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/crypto/cipher"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/page"
)
type (
CustomDomain struct {
ID gid.GID `db:"id"`
OrganizationID gid.GID `db:"organization_id"`
Domain string `db:"domain"`
HTTPChallengeToken *string `db:"http_challenge_token"`
HTTPChallengeKeyAuth *string `db:"http_challenge_key_auth"`
@@ -159,6 +160,7 @@ func (cd *CustomDomain) LoadByID(
q := `
SELECT
id,
organization_id,
domain,
http_challenge_token,
http_challenge_key_auth,
@@ -211,6 +213,7 @@ func (cd *CustomDomain) LoadByIDForUpdate(
q := `
SELECT
id,
organization_id,
domain,
http_challenge_token,
http_challenge_key_auth,
@@ -263,6 +266,7 @@ func (cd *CustomDomain) LoadByDomain(
q := `
SELECT
id,
organization_id,
domain,
http_challenge_token,
http_challenge_key_auth,
@@ -320,6 +324,7 @@ func (cd *CustomDomain) Insert(
INSERT INTO custom_domains (
id,
tenant_id,
organization_id,
domain,
http_challenge_token,
http_challenge_key_auth,
@@ -337,6 +342,7 @@ INSERT INTO custom_domains (
) VALUES (
@id,
@tenant_id,
@organization_id,
@domain,
@http_challenge_token,
@http_challenge_key_auth,
@@ -357,6 +363,7 @@ INSERT INTO custom_domains (
args := pgx.NamedArgs{
"id": cd.ID,
"tenant_id": scope.GetTenantID(),
"organization_id": cd.OrganizationID,
"domain": cd.Domain,
"http_challenge_token": cd.HTTPChallengeToken,
"http_challenge_key_auth": cd.HTTPChallengeKeyAuth,
@@ -487,6 +494,7 @@ func (cd *CustomDomain) LoadByHTTPChallengeToken(
q := `
SELECT
id,
organization_id,
domain,
http_challenge_token,
http_challenge_key_auth,
@@ -537,6 +545,7 @@ func (domains *CustomDomains) ListDomainsForRenewal(
q := `
SELECT
id,
organization_id,
domain,
http_challenge_token,
http_challenge_key_auth,
@@ -589,6 +598,7 @@ func (domains *CustomDomains) ListDomainsWithPendingHTTPChallenges(
q := `
SELECT
id,
organization_id,
domain,
http_challenge_token,
http_challenge_key_auth,
@@ -644,6 +654,7 @@ func (domains *CustomDomains) LoadActiveCertificates(
q := `
SELECT
id,
organization_id,
domain,
http_challenge_token,
http_challenge_key_auth,
@@ -693,6 +704,7 @@ func (domains *CustomDomains) ListStaleProvisioningDomains(
q := `
SELECT
id,
organization_id,
domain,
http_challenge_token,
http_challenge_key_auth,

View File

@@ -41,6 +41,7 @@ func (dv DatumVendors) Merge(
conn pg.Conn,
scope Scoper,
datumID gid.GID,
organizationID gid.GID,
vendorIDs []gid.GID,
) error {
q := `
@@ -49,6 +50,7 @@ WITH vendor_ids AS (
unnest(@vendor_ids::text[]) AS vendor_id,
@tenant_id AS tenant_id,
@datum_id AS datum_id,
@organization_id AS organization_id,
@created_at::timestamptz AS created_at
)
MERGE INTO data_vendors AS tgt
@@ -57,18 +59,19 @@ ON tgt.tenant_id = src.tenant_id
AND tgt.datum_id = src.datum_id
AND tgt.vendor_id = src.vendor_id
WHEN NOT MATCHED THEN
INSERT (tenant_id, datum_id, vendor_id, created_at)
VALUES (src.tenant_id, src.datum_id, src.vendor_id, src.created_at)
INSERT (tenant_id, datum_id, vendor_id, organization_id, created_at)
VALUES (src.tenant_id, src.datum_id, src.vendor_id, src.organization_id, src.created_at)
WHEN NOT MATCHED BY SOURCE
AND tgt.tenant_id = @tenant_id AND tgt.datum_id = @datum_id
THEN DELETE
`
args := pgx.StrictNamedArgs{
"tenant_id": scope.GetTenantID(),
"datum_id": datumID,
"created_at": time.Now(),
"vendor_ids": vendorIDs,
"tenant_id": scope.GetTenantID(),
"datum_id": datumID,
"organization_id": organizationID,
"created_at": time.Now(),
"vendor_ids": vendorIDs,
}
_, err := conn.Exec(ctx, q, args)
@@ -84,26 +87,29 @@ func (dv DatumVendors) Insert(
conn pg.Conn,
scope Scoper,
datumID gid.GID,
organizationID gid.GID,
vendorIDs []gid.GID,
) error {
q := `
WITH vendor_ids AS (
SELECT unnest(@vendor_ids::text[]) AS vendor_id
)
INSERT INTO data_vendors (tenant_id, datum_id, vendor_id, created_at)
INSERT INTO data_vendors (tenant_id, datum_id, vendor_id, organization_id, created_at)
SELECT
@tenant_id::text AS tenant_id,
@datum_id::text AS datum_id,
vendor_id,
@organization_id::text AS organization_id,
@created_at::timestamptz AS created_at
FROM vendor_ids
`
args := pgx.StrictNamedArgs{
"tenant_id": scope.GetTenantID(),
"datum_id": datumID,
"created_at": time.Now(),
"vendor_ids": vendorIDs,
"tenant_id": scope.GetTenantID(),
"datum_id": datumID,
"organization_id": organizationID,
"created_at": time.Now(),
"vendor_ids": vendorIDs,
}
_, err := conn.Exec(ctx, q, args)

View File

@@ -21,16 +21,17 @@ import (
"maps"
"time"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/page"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgconn"
"go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/page"
)
type (
DocumentVersion struct {
ID gid.GID `db:"id"`
OrganizationID gid.GID `db:"organization_id"`
DocumentID gid.GID `db:"document_id"`
Title string `db:"title"`
OwnerID gid.GID `db:"owner_id"`
@@ -81,6 +82,7 @@ func (p *DocumentVersions) LoadByDocumentID(
q := `
SELECT
id,
organization_id,
document_id,
title,
owner_id,
@@ -140,6 +142,7 @@ func (p *DocumentVersion) LoadByID(
q := `
SELECT
id,
organization_id,
document_id,
title,
owner_id,
@@ -190,6 +193,7 @@ func (p DocumentVersion) Insert(
INSERT INTO document_versions (
tenant_id,
id,
organization_id,
document_id,
title,
owner_id,
@@ -204,6 +208,7 @@ INSERT INTO document_versions (
VALUES (
@tenant_id,
@id,
@organization_id,
@document_id,
@title,
@owner_id,
@@ -217,18 +222,19 @@ VALUES (
)
`
args := pgx.StrictNamedArgs{
"tenant_id": scope.GetTenantID(),
"id": p.ID,
"document_id": p.DocumentID,
"title": p.Title,
"owner_id": p.OwnerID,
"version_number": p.VersionNumber,
"classification": p.Classification,
"content": p.Content,
"changelog": p.Changelog,
"status": p.Status,
"created_at": p.CreatedAt,
"updated_at": p.UpdatedAt,
"tenant_id": scope.GetTenantID(),
"id": p.ID,
"organization_id": p.OrganizationID,
"document_id": p.DocumentID,
"title": p.Title,
"owner_id": p.OwnerID,
"version_number": p.VersionNumber,
"classification": p.Classification,
"content": p.Content,
"changelog": p.Changelog,
"status": p.Status,
"created_at": p.CreatedAt,
"updated_at": p.UpdatedAt,
}
_, err := conn.Exec(ctx, q, args)
@@ -264,6 +270,7 @@ func (p *DocumentVersion) LoadByDocumentIDAndVersionNumber(
q := `
SELECT
id,
organization_id,
document_id,
title,
owner_id,
@@ -316,6 +323,7 @@ func (p *DocumentVersion) LoadLatestVersion(
q := `
SELECT
id,
organization_id,
document_id,
title,
owner_id,
@@ -366,6 +374,7 @@ func (p *DocumentVersion) LoadLatestPublishedVersion(
q := `
SELECT
id,
organization_id,
document_id,
title,
owner_id,

View File

@@ -21,16 +21,17 @@ import (
"maps"
"time"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/page"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgconn"
"go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/page"
)
type (
DocumentVersionSignature struct {
ID gid.GID `json:"id"`
OrganizationID gid.GID `json:"-"`
DocumentVersionID gid.GID `json:"document_version_id"`
State DocumentVersionSignatureState `json:"state"`
SignedBy gid.GID `json:"signed_by"`
@@ -87,6 +88,7 @@ func (pvs *DocumentVersionSignature) LoadByDocumentVersionIDAndSignatory(
q := `
SELECT
id,
organization_id,
document_version_id,
state,
signed_by,
@@ -132,6 +134,7 @@ func (pvs *DocumentVersionSignature) LoadByID(
q := `
SELECT
id,
organization_id,
document_version_id,
state,
signed_by,
@@ -175,6 +178,7 @@ func (pvs DocumentVersionSignature) Insert(
INSERT INTO document_version_signatures (
id,
tenant_id,
organization_id,
document_version_id,
state,
signed_by,
@@ -185,6 +189,7 @@ INSERT INTO document_version_signatures (
) VALUES (
@id,
@tenant_id,
@organization_id,
@document_version_id,
@state,
@signed_by,
@@ -198,6 +203,7 @@ INSERT INTO document_version_signatures (
args := pgx.StrictNamedArgs{
"id": pvs.ID,
"tenant_id": scope.GetTenantID(),
"organization_id": pvs.OrganizationID,
"document_version_id": pvs.DocumentVersionID,
"state": pvs.State,
"signed_by": pvs.SignedBy,
@@ -234,6 +240,7 @@ func (pvss *DocumentVersionSignatures) LoadByDocumentVersionID(
q := `
SELECT
id,
organization_id,
document_version_id,
state,
signed_by,
@@ -346,6 +353,7 @@ func (pvss *DocumentVersionSignaturesWithPeople) LoadByDocumentVersionIDWithPeop
WITH sigs AS (
SELECT
dvs.id,
dvs.organization_id,
dvs.tenant_id,
dvs.document_version_id,
dvs.state,
@@ -367,6 +375,7 @@ WITH sigs AS (
)
SELECT
id,
organization_id,
document_version_id,
state,
signed_by,

View File

@@ -20,9 +20,9 @@ import (
"fmt"
"time"
"go.probo.inc/probo/pkg/gid"
"github.com/jackc/pgx/v5"
"go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/gid"
)
type (

View File

@@ -68,3 +68,211 @@ const (
UserAPIKeyMembershipEntityType uint16 = 44
MeetingEntityType uint16 = 45
)
type EntityInfo struct {
Model string
Table string
}
var entityRegistry = map[uint16]EntityInfo{
OrganizationEntityType: {
Model: "Organization",
Table: "organizations",
},
FrameworkEntityType: {
Model: "Framework",
Table: "frameworks",
},
MeasureEntityType: {
Model: "Measure",
Table: "measures",
},
TaskEntityType: {
Model: "Task",
Table: "tasks",
},
EvidenceEntityType: {
Model: "Evidence",
Table: "evidences",
},
ConnectorEntityType: {
Model: "Connector",
Table: "connectors",
},
VendorRiskAssessmentEntityType: {
Model: "VendorRiskAssessment",
Table: "vendor_risk_assessments",
},
VendorEntityType: {
Model: "Vendor",
Table: "vendors",
},
PeopleEntityType: {
Model: "People",
Table: "peoples",
},
VendorComplianceReportEntityType: {
Model: "VendorComplianceReport",
Table: "vendor_compliance_reports",
},
DocumentEntityType: {
Model: "Document",
Table: "documents",
},
UserEntityType: {
Model: "User",
Table: "auth_users",
},
SessionEntityType: {
Model: "Session",
Table: "auth_sessions",
},
EmailEntityType: {
Model: "Email",
Table: "auth_emails",
},
ControlEntityType: {
Model: "Control",
Table: "controls",
},
RiskEntityType: {
Model: "Risk",
Table: "risks",
},
DocumentVersionEntityType: {
Model: "DocumentVersion",
Table: "document_versions",
},
DocumentVersionSignatureEntityType: {
Model: "DocumentVersionSignature",
Table: "document_version_signatures",
},
AssetEntityType: {
Model: "Asset",
Table: "assets",
},
DatumEntityType: {
Model: "Datum",
Table: "data",
},
AuditEntityType: {
Model: "Audit",
Table: "audits",
},
ReportEntityType: {
Model: "Report",
Table: "reports",
},
TrustCenterEntityType: {
Model: "TrustCenter",
Table: "trust_centers",
},
TrustCenterAccessEntityType: {
Model: "TrustCenterAccess",
Table: "trust_center_accesses",
},
VendorBusinessAssociateAgreementEntityType: {
Model: "VendorBusinessAssociateAgreement",
Table: "vendor_business_associate_agreements",
},
FileEntityType: {
Model: "File",
Table: "files",
},
VendorContactEntityType: {
Model: "VendorContact",
Table: "vendor_contacts",
},
VendorDataPrivacyAgreementEntityType: {
Model: "VendorDataPrivacyAgreement",
Table: "vendor_data_privacy_agreements",
},
NonconformityEntityType: {
Model: "Nonconformity",
Table: "nonconformities",
},
ObligationEntityType: {
Model: "Obligation",
Table: "obligations",
},
VendorServiceEntityType: {
Model: "VendorService",
Table: "vendor_services",
},
SnapshotEntityType: {
Model: "Snapshot",
Table: "snapshots",
},
ContinualImprovementEntityType: {
Model: "ContinualImprovement",
Table: "continual_improvements",
},
ProcessingActivityEntityType: {
Model: "ProcessingActivity",
Table: "processing_activities",
},
ExportJobEntityType: {
Model: "ExportJob",
Table: "export_jobs",
},
TrustCenterReferenceEntityType: {
Model: "TrustCenterReference",
Table: "trust_center_references",
},
TrustCenterDocumentAccessEntityType: {
Model: "",
Table: "trust_center_document_accesses",
},
CustomDomainEntityType: {
Model: "CustomDomain",
Table: "custom_domains",
},
InvitationEntityType: {
Model: "Invitation",
Table: "authz_invitations",
},
MembershipEntityType: {
Model: "Membership",
Table: "authz_memberships",
},
SlackMessageEntityType: {
Model: "SlackMessage",
Table: "slack_messages",
},
TrustCenterFileEntityType: {
Model: "TrustCenterFile",
Table: "trust_center_files",
},
SAMLConfigurationEntityType: {
Model: "SAMLConfiguration",
Table: "auth_saml_configurations",
},
UserAPIKeyEntityType: {
Model: "UserAPIKey",
Table: "auth_user_api_keys",
},
UserAPIKeyMembershipEntityType: {
Model: "UserAPIKeyMembership",
Table: "authz_api_keys_memberships",
},
MeetingEntityType: {
Model: "Meeting",
Table: "meetings",
},
}
func EntityTable(entityType uint16) (string, bool) {
info, ok := entityRegistry[entityType]
if !ok {
return "", false
}
return info.Table, true
}
func EntityModel(entityType uint16) (string, bool) {
info, ok := entityRegistry[entityType]
if !ok {
return "", false
}
return info.Model, true
}

View File

@@ -31,6 +31,7 @@ import (
type (
Evidence struct {
ID gid.GID `db:"id"`
OrganizationID gid.GID `db:"organization_id"`
MeasureID gid.GID `db:"measure_id"`
TaskID *gid.GID `db:"task_id"`
State EvidenceState `db:"state"`
@@ -141,6 +142,7 @@ INSERT INTO
evidences (
tenant_id,
id,
organization_id,
measure_id,
task_id,
reference_id,
@@ -155,6 +157,7 @@ INSERT INTO
VALUES (
@tenant_id,
@evidence_id,
@organization_id,
@measure_id,
@task_id,
@reference_id,
@@ -171,6 +174,7 @@ VALUES (
args := pgx.StrictNamedArgs{
"tenant_id": scope.GetTenantID(),
"evidence_id": e.ID,
"organization_id": e.OrganizationID,
"measure_id": e.MeasureID,
"task_id": e.TaskID,
"reference_id": e.ReferenceID,
@@ -208,6 +212,7 @@ func (e *Evidence) LoadByID(
q := `
SELECT
id,
organization_id,
task_id,
measure_id,
reference_id,
@@ -288,6 +293,7 @@ func (e *Evidences) LoadByMeasureID(
q := `
SELECT
id,
organization_id,
measure_id,
task_id,
reference_id,
@@ -369,6 +375,7 @@ func (e *Evidences) LoadByTaskID(
q := `
SELECT
id,
organization_id,
measure_id,
task_id,
reference_id,

View File

@@ -8,14 +8,15 @@ import (
"maps"
"time"
"go.probo.inc/probo/pkg/gid"
"github.com/jackc/pgx/v5"
"go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/gid"
)
type (
ExportJob struct {
ID gid.GID `db:"id"`
OrganizationID gid.GID `db:"organization_id"`
Type ExportJobType `db:"type"`
Arguments json.RawMessage `db:"arguments"`
Error *string `db:"error"`
@@ -54,6 +55,7 @@ func (ej *ExportJob) Insert(
q := `
INSERT INTO export_jobs (
id,
organization_id,
tenant_id,
type,
arguments,
@@ -63,6 +65,7 @@ INSERT INTO export_jobs (
created_at
) VALUES (
@id,
@organization_id,
@tenant_id,
@type,
@arguments,
@@ -73,6 +76,7 @@ INSERT INTO export_jobs (
)`
args := pgx.StrictNamedArgs{
"id": ej.ID,
"organization_id": ej.OrganizationID,
"tenant_id": scope.GetTenantID(),
"type": ej.Type,
"arguments": ej.Arguments,
@@ -126,6 +130,7 @@ func (ej *ExportJob) LoadByID(
q := `
SELECT
id,
organization_id,
type,
arguments,
error,
@@ -167,6 +172,7 @@ func (ej *ExportJob) LoadNextPendingForUpdateSkipLocked(
q := `
SELECT
id,
organization_id,
type,
arguments,
error,

View File

@@ -21,23 +21,24 @@ import (
"maps"
"time"
"go.probo.inc/probo/pkg/gid"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgconn"
"go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/gid"
)
type (
File struct {
ID gid.GID `db:"id"`
BucketName string `db:"bucket_name"`
MimeType string `db:"mime_type"`
FileName string `db:"file_name"`
FileKey string `db:"file_key"`
FileSize int64 `db:"file_size"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
DeletedAt *time.Time `db:"deleted_at"`
ID gid.GID `db:"id"`
OrganizationID gid.GID `db:"organization_id"`
BucketName string `db:"bucket_name"`
MimeType string `db:"mime_type"`
FileName string `db:"file_name"`
FileKey string `db:"file_key"`
FileSize int64 `db:"file_size"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
DeletedAt *time.Time `db:"deleted_at"`
}
Files []*File
@@ -68,6 +69,7 @@ func (f *File) LoadByID(
q := `
SELECT
id,
organization_id,
bucket_name,
mime_type,
file_name,
@@ -119,6 +121,7 @@ INSERT INTO
files (
id,
tenant_id,
organization_id,
bucket_name,
mime_type,
file_name,
@@ -131,6 +134,7 @@ INSERT INTO
VALUES (
@file_id,
@tenant_id,
@organization_id,
@bucket_name,
@mime_type,
@file_name,
@@ -143,16 +147,17 @@ VALUES (
`
args := pgx.StrictNamedArgs{
"file_id": f.ID,
"tenant_id": scope.GetTenantID(),
"bucket_name": f.BucketName,
"mime_type": f.MimeType,
"file_name": f.FileName,
"file_key": f.FileKey,
"file_size": f.FileSize,
"created_at": f.CreatedAt,
"updated_at": f.UpdatedAt,
"deleted_at": f.DeletedAt,
"file_id": f.ID,
"tenant_id": scope.GetTenantID(),
"organization_id": f.OrganizationID,
"bucket_name": f.BucketName,
"mime_type": f.MimeType,
"file_name": f.FileName,
"file_key": f.FileKey,
"file_size": f.FileSize,
"created_at": f.CreatedAt,
"updated_at": f.UpdatedAt,
"deleted_at": f.DeletedAt,
}
_, err := conn.Exec(ctx, q, args)

View File

@@ -21,10 +21,10 @@ import (
"maps"
"time"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/page"
"github.com/jackc/pgx/v5"
"go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/page"
)
type (
@@ -33,7 +33,7 @@ type (
OrganizationID gid.GID `db:"organization_id"`
Email string `db:"email"`
FullName string `db:"full_name"`
Role Role `db:"role"`
Role MembershipRole `db:"role"`
Status InvitationStatus `db:"status"`
ExpiresAt time.Time `db:"expires_at"`
AcceptedAt *time.Time `db:"accepted_at"`
@@ -43,11 +43,11 @@ type (
Invitations []*Invitation
InvitationData struct {
InvitationID gid.GID `json:"invitation_id"`
OrganizationID gid.GID `json:"organization_id"`
Email string `json:"email"`
FullName string `json:"full_name"`
Role Role `json:"role"`
InvitationID gid.GID `json:"invitation_id"`
OrganizationID gid.GID `json:"organization_id"`
Email string `json:"email"`
FullName string `json:"full_name"`
Role MembershipRole `json:"role"`
}
ErrInvitationNotFound struct {

View File

@@ -19,20 +19,19 @@ import (
"fmt"
)
type Role string
type MembershipRole string
const (
RoleOwner Role = "OWNER"
RoleAdmin Role = "ADMIN"
RoleMember Role = "MEMBER"
RoleViewer Role = "VIEWER"
MembershipRoleOwner MembershipRole = "OWNER"
MembershipRoleAdmin MembershipRole = "ADMIN"
MembershipRoleViewer MembershipRole = "VIEWER"
)
func (r Role) String() string {
func (r MembershipRole) String() string {
return string(r)
}
func (r *Role) Scan(value any) error {
func (r *MembershipRole) Scan(value any) error {
var s string
switch v := value.(type) {
case string:
@@ -40,24 +39,22 @@ func (r *Role) Scan(value any) error {
case []byte:
s = string(v)
default:
return fmt.Errorf("unsupported type for Role: %T", value)
return fmt.Errorf("unsupported type for MembershipRole: %T", value)
}
switch s {
case "OWNER":
*r = RoleOwner
*r = MembershipRoleOwner
case "ADMIN":
*r = RoleAdmin
case "MEMBER":
*r = RoleMember
*r = MembershipRoleAdmin
case "VIEWER":
*r = RoleViewer
*r = MembershipRoleViewer
default:
return fmt.Errorf("invalid Role value: %q", s)
return fmt.Errorf("invalid MembershipRole value: %q", s)
}
return nil
}
func (r Role) Value() (driver.Value, error) {
func (r MembershipRole) Value() (driver.Value, error) {
return r.String(), nil
}

View File

@@ -19,25 +19,26 @@ import (
"errors"
"fmt"
"maps"
"strings"
"time"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/page"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgconn"
"go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/page"
)
type (
Membership struct {
ID gid.GID `db:"id"`
UserID gid.GID `db:"user_id"`
OrganizationID gid.GID `db:"organization_id"`
Role Role `db:"role"`
FullName string `db:"full_name"`
EmailAddress string `db:"email_address"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
ID gid.GID `db:"id"`
UserID gid.GID `db:"user_id"`
OrganizationID gid.GID `db:"organization_id"`
Role MembershipRole `db:"role"`
FullName string `db:"full_name"`
EmailAddress string `db:"email_address"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}
Memberships []*Membership
@@ -185,6 +186,83 @@ JOIN
return nil
}
// LoadRoleByUserAndEntityID loads a user's role by querying any entity to extract its organization_id
func (m *Membership) LoadRoleByUserAndEntityID(
ctx context.Context,
conn pg.Conn,
scope Scoper,
userID gid.GID,
entityID gid.GID,
) error {
entityType := entityID.EntityType()
// For organization, the entity ID is the organization ID
if entityType == OrganizationEntityType {
return m.LoadByUserAndOrg(ctx, conn, scope, userID, entityID)
}
tableName, ok := EntityTable(entityType)
if !ok {
return fmt.Errorf("unsupported entity type for role lookup: %d", entityType)
}
// Build scope fragment with table alias to avoid ambiguity
scopeFragment := scope.SQLFragment()
// Replace column references with table-qualified versions
scopeFragment = strings.ReplaceAll(scopeFragment, "tenant_id =", "m.tenant_id =")
query := fmt.Sprintf(`
SELECT
m.id,
m.user_id,
m.organization_id,
m.role,
m.created_at,
m.updated_at
FROM
authz_memberships m
INNER JOIN %s e ON e.id = @entity_id
WHERE
%s
AND m.user_id = @user_id
AND m.organization_id = e.organization_id
LIMIT 1;
`, tableName, scopeFragment)
args := pgx.NamedArgs{
"user_id": userID,
"entity_id": entityID,
}
maps.Copy(args, scope.SQLArguments())
rows, err := conn.Query(ctx, query, args)
if err != nil {
return fmt.Errorf("cannot query membership by entity: %w", err)
}
defer rows.Close()
if !rows.Next() {
return &ErrMembershipNotFound{UserID: userID, OrgID: entityID}
}
var membership Membership
err = rows.Scan(
&membership.ID,
&membership.UserID,
&membership.OrganizationID,
&membership.Role,
&membership.CreatedAt,
&membership.UpdatedAt,
)
if err != nil {
return fmt.Errorf("cannot scan membership: %w", err)
}
*m = membership
return nil
}
func (m *Membership) LoadByUserAndOrg(
ctx context.Context,
conn pg.Conn,
@@ -195,17 +273,17 @@ func (m *Membership) LoadByUserAndOrg(
query := `
WITH mbr AS (
SELECT
id,
user_id,
organization_id,
role,
created_at,
updated_at
am.id,
am.user_id,
am.organization_id,
am.role,
am.created_at,
am.updated_at
FROM
authz_memberships
authz_memberships am
WHERE
user_id = @user_id
AND organization_id = @organization_id
am.user_id = @user_id
AND am.organization_id = @organization_id
AND %s
)
SELECT
@@ -223,7 +301,12 @@ JOIN
users u ON mbr.user_id = u.id
`
query = fmt.Sprintf(query, scope.SQLFragment())
// Build scope fragment with table alias
scopeFragment := scope.SQLFragment()
// Replace column references with table-qualified versions
scopeFragment = strings.ReplaceAll(scopeFragment, "tenant_id =", "am.tenant_id =")
query = fmt.Sprintf(query, scopeFragment)
args := pgx.StrictNamedArgs{
"user_id": userID,
@@ -468,67 +551,3 @@ WHERE
}
return count, nil
}
func LoadUserIDsByOrganizationID(
ctx context.Context,
conn pg.Conn,
scope Scoper,
organizationID gid.GID,
) ([]gid.GID, error) {
query := `
SELECT user_id
FROM authz_memberships
WHERE organization_id = @organization_id AND %s
`
query = fmt.Sprintf(query, scope.SQLFragment())
args := pgx.StrictNamedArgs{"organization_id": organizationID}
maps.Copy(args, scope.SQLArguments())
rows, err := conn.Query(ctx, query, args)
if err != nil {
return nil, fmt.Errorf("cannot query memberships: %w", err)
}
var userIDs []gid.GID
for rows.Next() {
var userID gid.GID
if err := rows.Scan(&userID); err != nil {
rows.Close()
return nil, fmt.Errorf("cannot scan user_id: %w", err)
}
userIDs = append(userIDs, userID)
}
rows.Close()
return userIDs, nil
}
func UpdateMembershipUserID(
ctx context.Context,
conn pg.Conn,
scope Scoper,
oldUserID gid.GID,
newUserID gid.GID,
organizationID gid.GID,
) error {
query := `
UPDATE authz_memberships
SET user_id = @new_user_id, updated_at = @updated_at
WHERE user_id = @old_user_id AND organization_id = @organization_id AND %s
`
query = fmt.Sprintf(query, scope.SQLFragment())
args := pgx.StrictNamedArgs{
"new_user_id": newUserID,
"old_user_id": oldUserID,
"organization_id": organizationID,
"updated_at": time.Now(),
}
maps.Copy(args, scope.SQLArguments())
_, err := conn.Exec(ctx, query, args)
if err != nil {
return fmt.Errorf("cannot update membership: %w", err)
}
return nil
}

View File

@@ -0,0 +1,210 @@
-- Set all existing memberships to OWNER role
UPDATE authz_memberships SET role = 'OWNER';
ALTER TABLE controls ADD COLUMN IF NOT EXISTS organization_id TEXT;
UPDATE controls
SET organization_id = organizations.id
FROM organizations
WHERE controls.tenant_id = organizations.tenant_id
AND controls.organization_id IS NULL;
ALTER TABLE controls ALTER COLUMN organization_id SET NOT NULL;
ALTER TABLE evidences ADD COLUMN IF NOT EXISTS organization_id TEXT;
UPDATE evidences
SET organization_id = organizations.id
FROM organizations
WHERE evidences.tenant_id = organizations.tenant_id
AND evidences.organization_id IS NULL;
ALTER TABLE evidences ALTER COLUMN organization_id SET NOT NULL;
ALTER TABLE files ADD COLUMN IF NOT EXISTS organization_id TEXT;
UPDATE files
SET organization_id = organizations.id
FROM organizations
WHERE files.tenant_id = organizations.tenant_id
AND files.organization_id IS NULL;
ALTER TABLE files ALTER COLUMN organization_id SET NOT NULL;
ALTER TABLE document_versions ADD COLUMN IF NOT EXISTS organization_id TEXT;
UPDATE document_versions
SET organization_id = organizations.id
FROM organizations
WHERE document_versions.tenant_id = organizations.tenant_id
AND document_versions.organization_id IS NULL;
ALTER TABLE document_versions ALTER COLUMN organization_id SET NOT NULL;
ALTER TABLE document_version_signatures ADD COLUMN IF NOT EXISTS organization_id TEXT;
UPDATE document_version_signatures
SET organization_id = organizations.id
FROM organizations
WHERE document_version_signatures.tenant_id = organizations.tenant_id
AND document_version_signatures.organization_id IS NULL;
ALTER TABLE document_version_signatures ALTER COLUMN organization_id SET NOT NULL;
ALTER TABLE trust_center_references ADD COLUMN IF NOT EXISTS organization_id TEXT;
UPDATE trust_center_references
SET organization_id = organizations.id
FROM organizations
WHERE trust_center_references.tenant_id = organizations.tenant_id
AND trust_center_references.organization_id IS NULL;
ALTER TABLE trust_center_references ALTER COLUMN organization_id SET NOT NULL;
ALTER TABLE trust_center_accesses ADD COLUMN IF NOT EXISTS organization_id TEXT;
UPDATE trust_center_accesses
SET organization_id = organizations.id
FROM organizations
WHERE trust_center_accesses.tenant_id = organizations.tenant_id
AND trust_center_accesses.organization_id IS NULL;
ALTER TABLE trust_center_accesses ALTER COLUMN organization_id SET NOT NULL;
ALTER TABLE trust_center_document_accesses ADD COLUMN IF NOT EXISTS organization_id TEXT;
UPDATE trust_center_document_accesses
SET organization_id = organizations.id
FROM organizations
WHERE trust_center_document_accesses.tenant_id = organizations.tenant_id
AND trust_center_document_accesses.organization_id IS NULL;
ALTER TABLE trust_center_document_accesses ALTER COLUMN organization_id SET NOT NULL;
ALTER TABLE vendor_services ADD COLUMN IF NOT EXISTS organization_id TEXT;
UPDATE vendor_services
SET organization_id = organizations.id
FROM organizations
WHERE vendor_services.tenant_id = organizations.tenant_id
AND vendor_services.organization_id IS NULL;
ALTER TABLE vendor_services ALTER COLUMN organization_id SET NOT NULL;
ALTER TABLE vendor_contacts ADD COLUMN IF NOT EXISTS organization_id TEXT;
UPDATE vendor_contacts
SET organization_id = organizations.id
FROM organizations
WHERE vendor_contacts.tenant_id = organizations.tenant_id
AND vendor_contacts.organization_id IS NULL;
ALTER TABLE vendor_contacts ALTER COLUMN organization_id SET NOT NULL;
ALTER TABLE vendor_risk_assessments ADD COLUMN IF NOT EXISTS organization_id TEXT;
UPDATE vendor_risk_assessments
SET organization_id = organizations.id
FROM organizations
WHERE vendor_risk_assessments.tenant_id = organizations.tenant_id
AND vendor_risk_assessments.organization_id IS NULL;
ALTER TABLE vendor_risk_assessments ALTER COLUMN organization_id SET NOT NULL;
ALTER TABLE reports ADD COLUMN IF NOT EXISTS organization_id TEXT;
UPDATE reports
SET organization_id = organizations.id
FROM organizations
WHERE reports.tenant_id = organizations.tenant_id
AND reports.organization_id IS NULL;
ALTER TABLE reports ALTER COLUMN organization_id SET NOT NULL;
ALTER TABLE custom_domains ADD COLUMN IF NOT EXISTS organization_id TEXT;
UPDATE custom_domains
SET organization_id = organizations.id
FROM organizations
WHERE custom_domains.tenant_id = organizations.tenant_id
AND custom_domains.organization_id IS NULL;
ALTER TABLE custom_domains ALTER COLUMN organization_id SET NOT NULL;
ALTER TABLE asset_vendors ADD COLUMN IF NOT EXISTS organization_id TEXT;
UPDATE asset_vendors
SET organization_id = organizations.id
FROM organizations
WHERE asset_vendors.tenant_id = organizations.tenant_id
AND asset_vendors.organization_id IS NULL;
ALTER TABLE asset_vendors ALTER COLUMN organization_id SET NOT NULL;
ALTER TABLE authz_api_keys_memberships ADD COLUMN IF NOT EXISTS organization_id TEXT;
UPDATE authz_api_keys_memberships
SET organization_id = organizations.id
FROM organizations
WHERE authz_api_keys_memberships.tenant_id = organizations.tenant_id
AND authz_api_keys_memberships.organization_id IS NULL;
ALTER TABLE authz_api_keys_memberships ALTER COLUMN organization_id SET NOT NULL;
ALTER TABLE controls_audits ADD COLUMN IF NOT EXISTS organization_id TEXT;
UPDATE controls_audits
SET organization_id = organizations.id
FROM organizations
WHERE controls_audits.tenant_id = organizations.tenant_id
AND controls_audits.organization_id IS NULL;
ALTER TABLE controls_audits ALTER COLUMN organization_id SET NOT NULL;
ALTER TABLE controls_documents ADD COLUMN IF NOT EXISTS organization_id TEXT;
UPDATE controls_documents
SET organization_id = organizations.id
FROM organizations
WHERE controls_documents.tenant_id = organizations.tenant_id
AND controls_documents.organization_id IS NULL;
ALTER TABLE controls_documents ALTER COLUMN organization_id SET NOT NULL;
ALTER TABLE controls_measures ADD COLUMN IF NOT EXISTS organization_id TEXT;
UPDATE controls_measures
SET organization_id = organizations.id
FROM organizations
WHERE controls_measures.tenant_id = organizations.tenant_id
AND controls_measures.organization_id IS NULL;
ALTER TABLE controls_measures ALTER COLUMN organization_id SET NOT NULL;
ALTER TABLE controls_snapshots ADD COLUMN IF NOT EXISTS organization_id TEXT;
UPDATE controls_snapshots
SET organization_id = organizations.id
FROM organizations
WHERE controls_snapshots.tenant_id = organizations.tenant_id
AND controls_snapshots.organization_id IS NULL;
ALTER TABLE controls_snapshots ALTER COLUMN organization_id SET NOT NULL;
ALTER TABLE data_vendors ADD COLUMN IF NOT EXISTS organization_id TEXT;
UPDATE data_vendors
SET organization_id = organizations.id
FROM organizations
WHERE data_vendors.tenant_id = organizations.tenant_id
AND data_vendors.organization_id IS NULL;
ALTER TABLE data_vendors ALTER COLUMN organization_id SET NOT NULL;
ALTER TABLE export_jobs ADD COLUMN IF NOT EXISTS organization_id TEXT;
UPDATE export_jobs
SET organization_id = organizations.id
FROM organizations
WHERE export_jobs.tenant_id = organizations.tenant_id
AND export_jobs.organization_id IS NULL;
ALTER TABLE export_jobs ALTER COLUMN organization_id SET NOT NULL;
ALTER TABLE processing_activity_vendors ADD COLUMN IF NOT EXISTS organization_id TEXT;
UPDATE processing_activity_vendors
SET organization_id = organizations.id
FROM organizations
WHERE processing_activity_vendors.tenant_id = organizations.tenant_id
AND processing_activity_vendors.organization_id IS NULL;
ALTER TABLE processing_activity_vendors ALTER COLUMN organization_id SET NOT NULL;
ALTER TABLE risks_documents ADD COLUMN IF NOT EXISTS organization_id TEXT;
UPDATE risks_documents
SET organization_id = organizations.id
FROM organizations
WHERE risks_documents.tenant_id = organizations.tenant_id
AND risks_documents.organization_id IS NULL;
ALTER TABLE risks_documents ALTER COLUMN organization_id SET NOT NULL;
ALTER TABLE risks_measures ADD COLUMN IF NOT EXISTS organization_id TEXT;
UPDATE risks_measures
SET organization_id = organizations.id
FROM organizations
WHERE risks_measures.tenant_id = organizations.tenant_id
AND risks_measures.organization_id IS NULL;
ALTER TABLE risks_measures ALTER COLUMN organization_id SET NOT NULL;
ALTER TABLE risks_obligations ADD COLUMN IF NOT EXISTS organization_id TEXT;
UPDATE risks_obligations
SET organization_id = organizations.id
FROM organizations
WHERE risks_obligations.tenant_id = organizations.tenant_id
AND risks_obligations.organization_id IS NULL;
ALTER TABLE risks_obligations ALTER COLUMN organization_id SET NOT NULL;
ALTER TABLE vendor_compliance_reports ADD COLUMN IF NOT EXISTS organization_id TEXT;
UPDATE vendor_compliance_reports
SET organization_id = organizations.id
FROM organizations
WHERE vendor_compliance_reports.tenant_id = organizations.tenant_id
AND vendor_compliance_reports.organization_id IS NULL;
ALTER TABLE vendor_compliance_reports ALTER COLUMN organization_id SET NOT NULL;

View File

@@ -237,6 +237,63 @@ ORDER BY
return nil
}
func (o *Organizations) LoadAllByUserIDWithRole(
ctx context.Context,
conn pg.Conn,
userID gid.GID,
role MembershipRole,
) error {
q := `
WITH user_org AS (
SELECT
organization_id
FROM
authz_memberships
WHERE
user_id = @user_id
AND role = @role
)
SELECT
tenant_id,
id,
name,
description,
website_url,
email,
headquarter_address,
custom_domain_id,
logo_file_id,
horizontal_logo_file_id,
created_at,
updated_at
FROM
organizations
INNER JOIN
user_org ON organizations.id = user_org.organization_id
ORDER BY
name ASC
`
args := pgx.StrictNamedArgs{
"user_id": userID,
"role": role,
}
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query organizations: %w", err)
}
organizations, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[Organization])
if err != nil {
return fmt.Errorf("cannot collect organizations: %w", err)
}
*o = organizations
return nil
}
func (o *Organizations) LoadAllByUserAPIKeyID(
ctx context.Context,
conn pg.Conn,

View File

@@ -20,21 +20,22 @@ import (
"maps"
"time"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/page"
"github.com/jackc/pgx/v5"
"go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/page"
)
type (
Report struct {
ID gid.GID `db:"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"`
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
@@ -49,6 +50,7 @@ func (r *Report) LoadByID(
q := `
SELECT
id,
organization_id,
object_key,
mime_type,
filename,
@@ -92,6 +94,7 @@ func (r *Report) Insert(
INSERT INTO reports (
id,
tenant_id,
organization_id,
object_key,
mime_type,
filename,
@@ -101,6 +104,7 @@ INSERT INTO reports (
) VALUES (
@id,
@tenant_id,
@organization_id,
@object_key,
@mime_type,
@filename,
@@ -111,14 +115,15 @@ INSERT INTO reports (
`
args := pgx.StrictNamedArgs{
"id": r.ID,
"tenant_id": scope.GetTenantID(),
"object_key": r.ObjectKey,
"mime_type": r.MimeType,
"filename": r.Filename,
"size": r.Size,
"created_at": r.CreatedAt,
"updated_at": r.UpdatedAt,
"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)

View File

@@ -27,10 +27,11 @@ import (
type (
RiskDocument struct {
RiskID gid.GID `db:"risk_id"`
DocumentID gid.GID `db:"document_id"`
TenantID gid.TenantID `db:"tenant_id"`
CreatedAt time.Time `db:"created_at"`
RiskID gid.GID `db:"risk_id"`
DocumentID gid.GID `db:"document_id"`
OrganizationID gid.GID `db:"organization_id"`
TenantID gid.TenantID `db:"tenant_id"`
CreatedAt time.Time `db:"created_at"`
}
RiskDocuments []*RiskDocument
@@ -46,22 +47,25 @@ INSERT INTO
risks_documents (
risk_id,
document_id,
organization_id,
tenant_id,
created_at
)
VALUES (
@risk_id,
@document_id,
@organization_id,
@tenant_id,
@created_at
);
`
args := pgx.StrictNamedArgs{
"risk_id": rp.RiskID,
"document_id": rp.DocumentID,
"tenant_id": scope.GetTenantID(),
"created_at": rp.CreatedAt,
"risk_id": rp.RiskID,
"document_id": rp.DocumentID,
"organization_id": rp.OrganizationID,
"tenant_id": scope.GetTenantID(),
"created_at": rp.CreatedAt,
}
_, err := conn.Exec(ctx, q, args)
return err

View File

@@ -20,17 +20,18 @@ import (
"maps"
"time"
"go.probo.inc/probo/pkg/gid"
"github.com/jackc/pgx/v5"
"go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/gid"
)
type (
RiskMeasure struct {
RiskID gid.GID `db:"risk_id"`
MeasureID gid.GID `db:"measure_id"`
TenantID gid.TenantID `db:"tenant_id"`
CreatedAt time.Time `db:"created_at"`
RiskID gid.GID `db:"risk_id"`
MeasureID gid.GID `db:"measure_id"`
OrganizationID gid.GID `db:"organization_id"`
TenantID gid.TenantID `db:"tenant_id"`
CreatedAt time.Time `db:"created_at"`
}
RiskMeasures []*RiskMeasure
@@ -46,22 +47,25 @@ INSERT INTO
risks_measures (
risk_id,
measure_id,
organization_id,
tenant_id,
created_at
)
VALUES (
@risk_id,
@measure_id,
@organization_id,
@tenant_id,
@created_at
);
`
args := pgx.StrictNamedArgs{
"risk_id": rm.RiskID,
"measure_id": rm.MeasureID,
"tenant_id": scope.GetTenantID(),
"created_at": rm.CreatedAt,
"risk_id": rm.RiskID,
"measure_id": rm.MeasureID,
"organization_id": rm.OrganizationID,
"tenant_id": scope.GetTenantID(),
"created_at": rm.CreatedAt,
}
_, err := conn.Exec(ctx, q, args)
return err

View File

@@ -27,9 +27,10 @@ import (
type (
RiskObligation struct {
RiskID gid.GID `db:"risk_id"`
ObligationID gid.GID `db:"obligation_id"`
CreatedAt time.Time `db:"created_at"`
RiskID gid.GID `db:"risk_id"`
ObligationID gid.GID `db:"obligation_id"`
OrganizationID gid.GID `db:"organization_id"`
CreatedAt time.Time `db:"created_at"`
}
RiskObligations []*RiskObligation
@@ -44,21 +45,24 @@ func (ro RiskObligation) Insert(
INSERT INTO risks_obligations (
risk_id,
obligation_id,
organization_id,
tenant_id,
created_at
) VALUES (
@risk_id,
@obligation_id,
@organization_id,
@tenant_id,
@created_at
)
`
args := pgx.StrictNamedArgs{
"risk_id": ro.RiskID,
"obligation_id": ro.ObligationID,
"tenant_id": scope.GetTenantID(),
"created_at": ro.CreatedAt,
"risk_id": ro.RiskID,
"obligation_id": ro.ObligationID,
"organization_id": ro.OrganizationID,
"tenant_id": scope.GetTenantID(),
"created_at": ro.CreatedAt,
}
_, err := conn.Exec(ctx, q, args)

View File

@@ -21,9 +21,9 @@ import (
"maps"
"time"
"go.probo.inc/probo/pkg/gid"
"github.com/jackc/pgx/v5"
"go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/gid"
)
type (

View File

@@ -22,16 +22,17 @@ import (
"maps"
"time"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/page"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgconn"
"go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/page"
)
type (
TrustCenterAccess struct {
ID gid.GID `db:"id"`
OrganizationID gid.GID `db:"organization_id"`
TenantID gid.TenantID `db:"tenant_id"`
TrustCenterID gid.GID `db:"trust_center_id"`
Email string `db:"email"`
@@ -82,6 +83,7 @@ func (tca *TrustCenterAccess) LoadByID(
q := `
SELECT
id,
organization_id,
tenant_id,
trust_center_id,
email,
@@ -135,6 +137,7 @@ func (tca *TrustCenterAccess) LoadByTrustCenterIDAndEmail(
q := `
SELECT
id,
organization_id,
tenant_id,
trust_center_id,
email,
@@ -191,6 +194,7 @@ func (tca *TrustCenterAccess) Insert(
INSERT INTO trust_center_accesses (
id,
tenant_id,
organization_id,
trust_center_id,
email,
name,
@@ -201,6 +205,7 @@ INSERT INTO trust_center_accesses (
) VALUES (
@id,
@tenant_id,
@organization_id,
@trust_center_id,
@email,
@name,
@@ -214,6 +219,7 @@ INSERT INTO trust_center_accesses (
args := pgx.StrictNamedArgs{
"id": tca.ID,
"tenant_id": tca.TenantID,
"organization_id": tca.OrganizationID,
"trust_center_id": tca.TrustCenterID,
"email": tca.Email,
"name": tca.Name,
@@ -317,6 +323,7 @@ func (tcas *TrustCenterAccesses) LoadByTrustCenterID(
q := `
SELECT
id,
organization_id,
tenant_id,
trust_center_id,
email,

View File

@@ -21,16 +21,17 @@ import (
"maps"
"time"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/page"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgconn"
"go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/page"
)
type (
TrustCenterDocumentAccess struct {
ID gid.GID `db:"id"`
OrganizationID gid.GID `db:"organization_id"`
TrustCenterAccessID gid.GID `db:"trust_center_access_id"`
DocumentID *gid.GID `db:"document_id"`
ReportID *gid.GID `db:"report_id"`
@@ -78,6 +79,7 @@ func (tcda *TrustCenterDocumentAccess) LoadByID(
q := `
SELECT
id,
organization_id,
trust_center_access_id,
document_id,
report_id,
@@ -127,6 +129,7 @@ func (tcda *TrustCenterDocumentAccess) LoadByTrustCenterAccessIDAndDocumentID(
q := `
SELECT
id,
organization_id,
trust_center_access_id,
document_id,
report_id,
@@ -177,6 +180,7 @@ func (tcda *TrustCenterDocumentAccess) LoadByTrustCenterAccessIDAndReportID(
q := `
SELECT
id,
organization_id,
trust_center_access_id,
document_id,
report_id,
@@ -226,6 +230,7 @@ func (tcda *TrustCenterDocumentAccess) Insert(
INSERT INTO trust_center_document_accesses (
id,
tenant_id,
organization_id,
trust_center_access_id,
document_id,
report_id,
@@ -237,6 +242,7 @@ INSERT INTO trust_center_document_accesses (
) VALUES (
@id,
@tenant_id,
@organization_id,
@trust_center_access_id,
@document_id,
@report_id,
@@ -251,6 +257,7 @@ INSERT INTO trust_center_document_accesses (
args := pgx.StrictNamedArgs{
"id": tcda.ID,
"tenant_id": scope.GetTenantID(),
"organization_id": tcda.OrganizationID,
"trust_center_access_id": tcda.TrustCenterAccessID,
"document_id": tcda.DocumentID,
"report_id": tcda.ReportID,
@@ -512,6 +519,7 @@ final_items AS (
SELECT
COALESCE(tcda.id, ai.item_id) AS id,
tcda.tenant_id,
(SELECT organization_id FROM organization) AS organization_id,
@trust_center_access_id AS trust_center_access_id,
ai.document_id,
ai.report_id,
@@ -532,6 +540,7 @@ final_items AS (
)
SELECT
id,
organization_id,
trust_center_access_id,
document_id,
report_id,
@@ -576,6 +585,7 @@ func (tcdas *TrustCenterDocumentAccesses) LoadAllByTrustCenterAccessID(
q := `
SELECT
id,
organization_id,
trust_center_access_id,
document_id,
report_id,
@@ -717,6 +727,7 @@ func (tcdas TrustCenterDocumentAccesses) BulkInsertDocumentAccesses(
conn pg.Conn,
scope Scoper,
trustCenterAccessID gid.GID,
organizationID gid.GID,
documentIDs []gid.GID,
requested bool,
createdAt time.Time,
@@ -730,6 +741,7 @@ WITH document_access_data AS (
SELECT
generate_gid(decode_base64_unpadded(@tenant_id), @trust_center_document_access_entity_type) AS id,
@tenant_id AS tenant_id,
@organization_id AS organization_id,
@trust_center_access_id AS trust_center_access_id,
unnest(@document_ids::text[]) AS document_id,
null::text AS report_id,
@@ -740,7 +752,7 @@ WITH document_access_data AS (
@updated_at::timestamptz AS updated_at
)
INSERT INTO trust_center_document_accesses (
id, tenant_id, trust_center_access_id, document_id, report_id, trust_center_file_id, active, requested, created_at, updated_at
id, tenant_id, organization_id, trust_center_access_id, document_id, report_id, trust_center_file_id, active, requested, created_at, updated_at
)
SELECT * FROM document_access_data
ON CONFLICT DO NOTHING
@@ -748,6 +760,7 @@ ON CONFLICT DO NOTHING
args := pgx.StrictNamedArgs{
"tenant_id": scope.GetTenantID(),
"organization_id": organizationID,
"trust_center_access_id": trustCenterAccessID,
"document_ids": documentIDs,
"trust_center_document_access_entity_type": TrustCenterDocumentAccessEntityType,
@@ -768,6 +781,7 @@ func (tcdas TrustCenterDocumentAccesses) BulkInsertReportAccesses(
conn pg.Conn,
scope Scoper,
trustCenterAccessID gid.GID,
organizationID gid.GID,
reportIDs []gid.GID,
requested bool,
createdAt time.Time,
@@ -781,6 +795,7 @@ WITH report_access_data AS (
SELECT
generate_gid(decode_base64_unpadded(@tenant_id), @trust_center_document_access_entity_type) AS id,
@tenant_id AS tenant_id,
@organization_id AS organization_id,
@trust_center_access_id AS trust_center_access_id,
null::text AS document_id,
unnest(@report_ids::text[]) AS report_id,
@@ -791,14 +806,15 @@ WITH report_access_data AS (
@updated_at::timestamptz AS updated_at
)
INSERT INTO trust_center_document_accesses (
id, tenant_id, trust_center_access_id, document_id, report_id, trust_center_file_id, active, requested, created_at, updated_at
id, tenant_id, organization_id, trust_center_access_id, document_id, report_id, trust_center_file_id, active, requested, created_at, updated_at
)
SELECT * FROM report_access_data
ON CONFLICT DO NOTHING
`
args := pgx.StrictNamedArgs{
"tenant_id": scope.GetTenantID(),
"tenant_id": scope.GetTenantID(),
"organization_id": organizationID,
"trust_center_document_access_entity_type": TrustCenterDocumentAccessEntityType,
"trust_center_access_id": trustCenterAccessID,
"report_ids": reportIDs,
@@ -903,6 +919,7 @@ func (tcdas TrustCenterDocumentAccesses) BulkInsertTrustCenterFileAccesses(
conn pg.Conn,
scope Scoper,
trustCenterAccessID gid.GID,
organizationID gid.GID,
trustCenterFileIDs []gid.GID,
requested bool,
createdAt time.Time,
@@ -912,6 +929,7 @@ WITH trust_center_file_access_data AS (
SELECT
generate_gid(decode_base64_unpadded(@tenant_id), @trust_center_document_access_entity_type) AS id,
@tenant_id AS tenant_id,
@organization_id AS organization_id,
@trust_center_access_id AS trust_center_access_id,
null::text AS document_id,
null::text AS report_id,
@@ -922,14 +940,15 @@ WITH trust_center_file_access_data AS (
@updated_at::timestamptz AS updated_at
)
INSERT INTO trust_center_document_accesses (
id, tenant_id, trust_center_access_id, document_id, report_id, trust_center_file_id, active, requested, created_at, updated_at
id, tenant_id, organization_id, trust_center_access_id, document_id, report_id, trust_center_file_id, active, requested, created_at, updated_at
)
SELECT * FROM trust_center_file_access_data
ON CONFLICT DO NOTHING
`
args := pgx.StrictNamedArgs{
"tenant_id": scope.GetTenantID(),
"tenant_id": scope.GetTenantID(),
"organization_id": organizationID,
"trust_center_document_access_entity_type": TrustCenterDocumentAccessEntityType,
"trust_center_access_id": trustCenterAccessID,
"trust_center_file_ids": trustCenterFileIDs,

View File

@@ -30,15 +30,16 @@ import (
type (
TrustCenterReference struct {
ID gid.GID `db:"id"`
TrustCenterID gid.GID `db:"trust_center_id"`
Name string `db:"name"`
Description *string `db:"description"`
WebsiteURL string `db:"website_url"`
LogoFileID gid.GID `db:"logo_file_id"`
Rank int `db:"rank"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
ID gid.GID `db:"id"`
OrganizationID gid.GID `db:"organization_id"`
TrustCenterID gid.GID `db:"trust_center_id"`
Name string `db:"name"`
Description *string `db:"description"`
WebsiteURL string `db:"website_url"`
LogoFileID gid.GID `db:"logo_file_id"`
Rank int `db:"rank"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}
TrustCenterReferences []*TrustCenterReference
@@ -83,6 +84,7 @@ func (t *TrustCenterReference) LoadByID(
q := `
SELECT
id,
organization_id,
trust_center_id,
name,
description,
@@ -128,6 +130,7 @@ INSERT INTO
trust_center_references (
tenant_id,
id,
organization_id,
trust_center_id,
name,
description,
@@ -140,6 +143,7 @@ INSERT INTO
VALUES (
@tenant_id,
@id,
@organization_id,
@trust_center_id,
@name,
@description,
@@ -155,6 +159,7 @@ RETURNING rank;
args := pgx.StrictNamedArgs{
"tenant_id": scope.GetTenantID(),
"id": t.ID,
"organization_id": t.OrganizationID,
"trust_center_id": t.TrustCenterID,
"name": t.Name,
"description": t.Description,
@@ -308,6 +313,7 @@ func (t *TrustCenterReferences) LoadByTrustCenterID(
q := `
SELECT
id,
organization_id,
trust_center_id,
name,
description,

View File

@@ -28,16 +28,17 @@ import (
type (
VendorComplianceReport struct {
ID gid.GID
VendorID gid.GID
ReportDate time.Time
ValidUntil *time.Time
ReportName string
ReportFileId *gid.GID
SnapshotID *gid.GID
SourceID *gid.GID
CreatedAt time.Time
UpdatedAt time.Time
ID gid.GID `db:"id"`
OrganizationID gid.GID `db:"organization_id"`
VendorID gid.GID `db:"vendor_id"`
ReportDate time.Time `db:"report_date"`
ValidUntil *time.Time `db:"valid_until"`
ReportName string `db:"report_name"`
ReportFileId *gid.GID `db:"report_file_id"`
SnapshotID *gid.GID `db:"snapshot_id"`
SourceID *gid.GID `db:"source_id"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}
VendorComplianceReports []*VendorComplianceReport
@@ -157,6 +158,7 @@ func (vcr *VendorComplianceReport) Insert(
INSERT INTO
vendor_compliance_reports (
id,
organization_id,
tenant_id,
vendor_id,
report_date,
@@ -168,6 +170,7 @@ INSERT INTO
)
VALUES (
@id,
@organization_id,
@tenant_id,
@vendor_id,
@report_date,
@@ -179,15 +182,16 @@ VALUES (
)
`
args := pgx.NamedArgs{
"id": vcr.ID,
"tenant_id": scope.GetTenantID(),
"vendor_id": vcr.VendorID,
"report_date": vcr.ReportDate,
"valid_until": vcr.ValidUntil,
"report_name": vcr.ReportName,
"report_file_id": vcr.ReportFileId,
"created_at": vcr.CreatedAt,
"updated_at": vcr.UpdatedAt,
"id": vcr.ID,
"organization_id": vcr.OrganizationID,
"tenant_id": scope.GetTenantID(),
"vendor_id": vcr.VendorID,
"report_date": vcr.ReportDate,
"valid_until": vcr.ValidUntil,
"report_name": vcr.ReportName,
"report_file_id": vcr.ReportFileId,
"created_at": vcr.CreatedAt,
"updated_at": vcr.UpdatedAt,
}
_, err := conn.Exec(ctx, q, args)

View File

@@ -21,24 +21,25 @@ import (
"maps"
"time"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/page"
"github.com/jackc/pgx/v5"
"go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/page"
)
type (
VendorContact struct {
ID gid.GID `db:"id"`
VendorID gid.GID `db:"vendor_id"`
FullName *string `db:"full_name"`
Email *string `db:"email"`
Phone *string `db:"phone"`
Role *string `db:"role"`
SnapshotID *gid.GID `db:"snapshot_id"`
SourceID *gid.GID `db:"source_id"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
ID gid.GID `db:"id"`
OrganizationID gid.GID `db:"organization_id"`
VendorID gid.GID `db:"vendor_id"`
FullName *string `db:"full_name"`
Email *string `db:"email"`
Phone *string `db:"phone"`
Role *string `db:"role"`
SnapshotID *gid.GID `db:"snapshot_id"`
SourceID *gid.GID `db:"source_id"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}
VendorContacts []*VendorContact
@@ -74,6 +75,7 @@ func (vc *VendorContact) LoadByID(
q := `
SELECT
id,
organization_id,
vendor_id,
full_name,
email,
@@ -126,6 +128,7 @@ func (vc *VendorContacts) LoadByVendorID(
q := `
SELECT
id,
organization_id,
vendor_id,
full_name,
email,
@@ -176,6 +179,7 @@ INSERT INTO
vendor_contacts (
tenant_id,
id,
organization_id,
vendor_id,
full_name,
email,
@@ -187,6 +191,7 @@ INSERT INTO
VALUES (
@tenant_id,
@vendor_contact_id,
@organization_id,
@vendor_id,
@full_name,
@email,
@@ -200,6 +205,7 @@ VALUES (
args := pgx.StrictNamedArgs{
"tenant_id": scope.GetTenantID(),
"vendor_contact_id": vc.ID,
"organization_id": vc.OrganizationID,
"vendor_id": vc.VendorID,
"full_name": vc.FullName,
"email": vc.Email,

View File

@@ -20,16 +20,17 @@ import (
"maps"
"time"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/page"
"github.com/jackc/pgx/v5"
"go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/page"
)
type (
// RiskAssessment represents a point-in-time risk assessment for a vendor
VendorRiskAssessment struct {
ID gid.GID `db:"id"`
OrganizationID gid.GID `db:"organization_id"`
VendorID gid.GID `db:"vendor_id"`
ExpiresAt time.Time `db:"expires_at"`
DataSensitivity DataSensitivity `db:"data_sensitivity"`
@@ -66,6 +67,7 @@ INSERT INTO
vendor_risk_assessments (
tenant_id,
id,
organization_id,
vendor_id,
expires_at,
data_sensitivity,
@@ -77,6 +79,7 @@ INSERT INTO
VALUES (
@tenant_id,
@id,
@organization_id,
@vendor_id,
@expires_at,
@data_sensitivity,
@@ -90,6 +93,7 @@ VALUES (
args := pgx.StrictNamedArgs{
"tenant_id": scope.GetTenantID(),
"id": r.ID,
"organization_id": r.OrganizationID,
"vendor_id": r.VendorID,
"expires_at": r.ExpiresAt,
"data_sensitivity": r.DataSensitivity,
@@ -112,6 +116,7 @@ func (r *VendorRiskAssessment) LoadByID(
q := `
SELECT
id,
organization_id,
vendor_id,
expires_at,
data_sensitivity,
@@ -160,6 +165,7 @@ func (r *VendorRiskAssessment) LoadLatestByVendorID(
q := `
SELECT
id,
organization_id,
vendor_id,
expires_at,
data_sensitivity,
@@ -211,6 +217,7 @@ func (r *VendorRiskAssessments) LoadByVendorID(
q := `
SELECT
id,
organization_id,
vendor_id,
expires_at,
data_sensitivity,

View File

@@ -21,22 +21,23 @@ import (
"maps"
"time"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/page"
"github.com/jackc/pgx/v5"
"go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/page"
)
type (
VendorService struct {
ID gid.GID `db:"id"`
VendorID gid.GID `db:"vendor_id"`
Name string `db:"name"`
Description *string `db:"description"`
SnapshotID *gid.GID `db:"snapshot_id"`
SourceID *gid.GID `db:"source_id"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
ID gid.GID `db:"id"`
OrganizationID gid.GID `db:"organization_id"`
VendorID gid.GID `db:"vendor_id"`
Name string `db:"name"`
Description *string `db:"description"`
SnapshotID *gid.GID `db:"snapshot_id"`
SourceID *gid.GID `db:"source_id"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}
VendorServices []*VendorService
@@ -70,6 +71,7 @@ func (vs *VendorService) LoadByID(
q := `
SELECT
id,
organization_id,
vendor_id,
name,
description,
@@ -120,6 +122,7 @@ func (vs *VendorServices) LoadByVendorID(
q := `
SELECT
id,
organization_id,
vendor_id,
name,
description,
@@ -168,6 +171,7 @@ INSERT INTO
vendor_services (
tenant_id,
id,
organization_id,
vendor_id,
name,
description,
@@ -177,6 +181,7 @@ INSERT INTO
VALUES (
@tenant_id,
@vendor_service_id,
@organization_id,
@vendor_id,
@name,
@description,
@@ -188,6 +193,7 @@ VALUES (
args := pgx.StrictNamedArgs{
"tenant_id": scope.GetTenantID(),
"vendor_service_id": vs.ID,
"organization_id": vs.OrganizationID,
"vendor_id": vs.VendorID,
"name": vs.Name,
"description": vs.Description,