Rewrite identity and access management

Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
Bryan Frimin
2025-12-03 19:23:15 +01:00
parent 4ed3f5a067
commit 74fc3b8cd1
201 changed files with 32895 additions and 23649 deletions

View File

@@ -20,12 +20,12 @@ import (
"github.com/aws/aws-sdk-go-v2/service/s3"
"go.gearno.de/kit/log"
"go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/auth"
"go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/crypto/cipher"
"go.probo.inc/probo/pkg/filemanager"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/html2pdf"
"go.probo.inc/probo/pkg/iam"
"go.probo.inc/probo/pkg/probo"
"go.probo.inc/probo/pkg/slack"
)
@@ -38,19 +38,20 @@ type (
}
Service struct {
pg *pg.Client
s3 *s3.Client
bucket string
proboSvc *probo.Service
encryptionKey cipher.EncryptionKey
tokenSecret string
baseURL string
auth *auth.Service
html2pdfConverter *html2pdf.Converter
fileManager *filemanager.Service
logger *log.Logger
trustConfig TrustConfig
slack *slack.Service
pg *pg.Client
s3 *s3.Client
bucket string
proboSvc *probo.Service
encryptionKey cipher.EncryptionKey
tokenSecret string
slackSigningSecret string
baseURL string
iam *iam.Service
html2pdfConverter *html2pdf.Converter
fileManager *filemanager.Service
logger *log.Logger
trustConfig TrustConfig
slack *slack.Service
}
TenantService struct {
@@ -62,7 +63,7 @@ type (
encryptionKey cipher.EncryptionKey
tokenSecret string
baseURL string
auth *auth.Service
iam *iam.Service
html2pdfConverter *html2pdf.Converter
fileManager *filemanager.Service
logger *log.Logger
@@ -88,7 +89,8 @@ func NewService(
baseURL string,
encryptionKey cipher.EncryptionKey,
tokenSecret string,
auth *auth.Service,
slackSigningSecret string,
iam *iam.Service,
html2pdfConverter *html2pdf.Converter,
fileManagerService *filemanager.Service,
logger *log.Logger,
@@ -96,18 +98,19 @@ func NewService(
slack *slack.Service,
) *Service {
return &Service{
pg: pgClient,
s3: s3Client,
bucket: bucket,
encryptionKey: encryptionKey,
tokenSecret: tokenSecret,
baseURL: baseURL,
auth: auth,
html2pdfConverter: html2pdfConverter,
fileManager: fileManagerService,
logger: logger,
trustConfig: trustConfig,
slack: slack,
pg: pgClient,
s3: s3Client,
bucket: bucket,
encryptionKey: encryptionKey,
tokenSecret: tokenSecret,
slackSigningSecret: slackSigningSecret,
baseURL: baseURL,
iam: iam,
html2pdfConverter: html2pdfConverter,
fileManager: fileManagerService,
logger: logger,
trustConfig: trustConfig,
slack: slack,
}
}
@@ -121,7 +124,7 @@ func (s *Service) WithTenant(tenantID gid.TenantID) *TenantService {
encryptionKey: s.encryptionKey,
tokenSecret: s.tokenSecret,
baseURL: s.baseURL,
auth: s.auth,
iam: s.iam,
html2pdfConverter: s.html2pdfConverter,
fileManager: s.fileManager,
logger: s.logger,
@@ -133,7 +136,7 @@ func (s *Service) WithTenant(tenantID gid.TenantID) *TenantService {
tenantService.Audits = &AuditService{svc: tenantService}
tenantService.Vendors = &VendorService{svc: tenantService}
tenantService.Frameworks = &FrameworkService{svc: tenantService}
tenantService.TrustCenterAccesses = &TrustCenterAccessService{svc: tenantService, auth: s.auth, logger: s.logger}
tenantService.TrustCenterAccesses = &TrustCenterAccessService{svc: tenantService, iamSvc: s.iam, logger: s.logger}
tenantService.TrustCenterReferences = &TrustCenterReferenceService{svc: tenantService}
tenantService.TrustCenterFiles = &TrustCenterFileService{svc: tenantService}
tenantService.Reports = &ReportService{svc: tenantService}

View File

@@ -25,9 +25,9 @@ import (
"go.gearno.de/kit/log"
"go.gearno.de/kit/pg"
"go.probo.inc/probo/packages/emails"
"go.probo.inc/probo/pkg/auth"
"go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/iam"
"go.probo.inc/probo/pkg/mail"
"go.probo.inc/probo/pkg/probo"
"go.probo.inc/probo/pkg/statelesstoken"
@@ -37,14 +37,14 @@ import (
type (
TrustCenterAccessService struct {
svc *TenantService
auth *auth.Service
iamSvc *iam.Service
logger *log.Logger
}
TrustCenterAccessRequest struct {
TrustCenterID gid.GID
Email mail.Addr
Name *string
Name string
DocumentIDs []gid.GID
ReportIDs []gid.GID
TrustCenterFileIDs []gid.GID
@@ -91,151 +91,151 @@ func (s TrustCenterAccessService) Request(
return nil, fmt.Errorf("invalid request arguments: %w", err)
}
now := time.Now()
var (
now = time.Now()
access *coredata.TrustCenterAccess
trustCenter *coredata.TrustCenter
)
var access *coredata.TrustCenterAccess
var trustCenter *coredata.TrustCenter
err := s.svc.pg.WithTx(ctx, func(tx pg.Conn) error {
var organizationID gid.GID
trustCenter = &coredata.TrustCenter{}
if err := trustCenter.LoadByID(ctx, tx, s.svc.scope, req.TrustCenterID); err != nil {
return fmt.Errorf("cannot load trust center: %w", err)
}
organizationID = trustCenter.OrganizationID
documentIDs := req.DocumentIDs
if req.DocumentIDs == nil {
var allDocuments coredata.Documents
filter := coredata.NewDocumentTrustCenterFilter()
if err := allDocuments.LoadAllByOrganizationID(ctx, tx, s.svc.scope, organizationID, filter); err != nil {
return fmt.Errorf("cannot list documents: %w", err)
err := s.svc.pg.WithTx(
ctx,
func(tx pg.Conn) error {
trustCenter = &coredata.TrustCenter{}
if err := trustCenter.LoadByID(ctx, tx, s.svc.scope, req.TrustCenterID); err != nil {
return fmt.Errorf("cannot load trust center: %w", err)
}
for _, doc := range allDocuments {
documentIDs = append(documentIDs, doc.ID)
}
}
// TODO: load document to ensure they are requestable
reportIDs := req.ReportIDs
if req.ReportIDs == nil {
var allAudits coredata.Audits
auditFilter := coredata.NewAuditTrustCenterFilter()
organizationID := trustCenter.OrganizationID
if err := allAudits.LoadAllByOrganizationID(ctx, tx, s.svc.scope, organizationID, auditFilter); err != nil {
return fmt.Errorf("cannot list audits: %w", err)
}
documentIDs := req.DocumentIDs
if req.DocumentIDs == nil {
var allDocuments coredata.Documents
filter := coredata.NewDocumentTrustCenterFilter()
for _, audit := range allAudits {
if audit.ReportID != nil {
reportIDs = append(reportIDs, *audit.ReportID)
if err := allDocuments.LoadAllByOrganizationID(ctx, tx, s.svc.scope, organizationID, filter); err != nil {
return fmt.Errorf("cannot list documents: %w", err)
}
for _, doc := range allDocuments {
documentIDs = append(documentIDs, doc.ID)
}
}
}
trustCenterFileIDs := req.TrustCenterFileIDs
if req.TrustCenterFileIDs == nil {
var allTrustCenterFiles coredata.TrustCenterFiles
filter := coredata.NewTrustCenterFileFilter(
coredata.WithTrustCenterFileVisibilities(coredata.TrustCenterVisibilityPrivate, coredata.TrustCenterVisibilityNone),
)
reportIDs := req.ReportIDs
if req.ReportIDs == nil {
var allAudits coredata.Audits
auditFilter := coredata.NewAuditTrustCenterFilter()
if err := allTrustCenterFiles.LoadAllByOrganizationID(ctx, tx, s.svc.scope, organizationID, filter); err != nil {
return fmt.Errorf("cannot list trust center files: %w", err)
if err := allAudits.LoadAllByOrganizationID(ctx, tx, s.svc.scope, organizationID, auditFilter); err != nil {
return fmt.Errorf("cannot list audits: %w", err)
}
for _, audit := range allAudits {
if audit.ReportID != nil {
reportIDs = append(reportIDs, *audit.ReportID)
}
}
}
for _, file := range allTrustCenterFiles {
trustCenterFileIDs = append(trustCenterFileIDs, file.ID)
}
}
trustCenterFileIDs := req.TrustCenterFileIDs
if req.TrustCenterFileIDs == nil {
var allTrustCenterFiles coredata.TrustCenterFiles
filter := coredata.NewTrustCenterFileFilter(
coredata.WithTrustCenterFileVisibilities(coredata.TrustCenterVisibilityPrivate, coredata.TrustCenterVisibilityNone),
)
existingAccess := &coredata.TrustCenterAccess{}
err := existingAccess.LoadByTrustCenterIDAndEmail(ctx, tx, s.svc.scope, req.TrustCenterID, req.Email)
if err := allTrustCenterFiles.LoadAllByOrganizationID(ctx, tx, s.svc.scope, organizationID, filter); err != nil {
return fmt.Errorf("cannot list trust center files: %w", err)
}
if err == nil {
access = existingAccess
} else {
var notFoundErr *coredata.ErrTrustCenterAccessNotFound
if !errors.As(err, &notFoundErr) {
return fmt.Errorf("cannot load trust center access: %w", err)
for _, file := range allTrustCenterFiles {
trustCenterFileIDs = append(trustCenterFileIDs, file.ID)
}
}
if req.Name == nil || *req.Name == "" {
return fmt.Errorf("name is required for new access requests")
existingAccess := &coredata.TrustCenterAccess{}
err := existingAccess.LoadByTrustCenterIDAndEmail(ctx, tx, s.svc.scope, req.TrustCenterID, req.Email)
if err == nil {
access = existingAccess
} else {
if !errors.Is(err, coredata.ErrResourceNotFound) {
return fmt.Errorf("cannot load trust center access: %w", err)
}
access = &coredata.TrustCenterAccess{
ID: gid.New(s.svc.scope.GetTenantID(), coredata.TrustCenterAccessEntityType),
OrganizationID: organizationID,
TenantID: s.svc.scope.GetTenantID(),
TrustCenterID: req.TrustCenterID,
Email: req.Email,
Name: req.Name,
Active: false,
HasAcceptedNonDisclosureAgreement: false,
CreatedAt: now,
UpdatedAt: now,
}
if err := access.Insert(ctx, tx, s.svc.scope); err != nil {
return fmt.Errorf("cannot insert trust center access: %w", err)
}
}
access = &coredata.TrustCenterAccess{
ID: gid.New(s.svc.scope.GetTenantID(), coredata.TrustCenterAccessEntityType),
OrganizationID: organizationID,
TenantID: s.svc.scope.GetTenantID(),
TrustCenterID: req.TrustCenterID,
Email: req.Email,
Name: *req.Name,
Active: false,
HasAcceptedNonDisclosureAgreement: false,
CreatedAt: now,
UpdatedAt: now,
var existingAccesses coredata.TrustCenterDocumentAccesses
if err := existingAccesses.LoadAllByTrustCenterAccessID(ctx, tx, s.svc.scope, access.ID); err != nil {
return fmt.Errorf("cannot load existing access records: %w", err)
}
if err := access.Insert(ctx, tx, s.svc.scope); err != nil {
return fmt.Errorf("cannot insert trust center access: %w", err)
existingDocumentIDs, existingReportIDs, existingTrustCenterFileIDs := extractExistingIDs(existingAccesses)
newDocumentIDs := filterExistingIDs(documentIDs, existingDocumentIDs)
newReportIDs := filterExistingIDs(reportIDs, existingReportIDs)
newTrustCenterFileIDs := filterExistingIDs(trustCenterFileIDs, existingTrustCenterFileIDs)
var accesses coredata.TrustCenterDocumentAccesses
if err := accesses.BulkInsertDocumentAccesses(
ctx,
tx,
s.svc.scope,
access.ID,
access.OrganizationID,
newDocumentIDs,
coredata.TrustCenterDocumentAccessStatusRequested,
now,
); err != nil {
return fmt.Errorf("cannot bulk insert trust center document accesses: %w", err)
}
}
var existingAccesses coredata.TrustCenterDocumentAccesses
if err := existingAccesses.LoadAllByTrustCenterAccessID(ctx, tx, s.svc.scope, access.ID); err != nil {
return fmt.Errorf("cannot load existing access records: %w", err)
}
if err := accesses.BulkInsertReportAccesses(
ctx,
tx,
s.svc.scope,
access.ID,
access.OrganizationID,
newReportIDs,
coredata.TrustCenterDocumentAccessStatusRequested,
now,
); err != nil {
return fmt.Errorf("cannot bulk insert trust center report accesses: %w", err)
}
existingDocumentIDs, existingReportIDs, existingTrustCenterFileIDs := extractExistingIDs(existingAccesses)
newDocumentIDs := filterExistingIDs(documentIDs, existingDocumentIDs)
newReportIDs := filterExistingIDs(reportIDs, existingReportIDs)
newTrustCenterFileIDs := filterExistingIDs(trustCenterFileIDs, existingTrustCenterFileIDs)
if err := accesses.BulkInsertTrustCenterFileAccesses(
ctx,
tx,
s.svc.scope,
access.ID,
access.OrganizationID,
newTrustCenterFileIDs,
coredata.TrustCenterDocumentAccessStatusRequested,
now,
); err != nil {
return fmt.Errorf("cannot bulk insert trust center file accesses: %w", err)
}
var accesses coredata.TrustCenterDocumentAccesses
if err := accesses.BulkInsertDocumentAccesses(
ctx,
tx,
s.svc.scope,
access.ID,
access.OrganizationID,
newDocumentIDs,
coredata.TrustCenterDocumentAccessStatusRequested,
now,
); err != nil {
return fmt.Errorf("cannot bulk insert trust center document accesses: %w", err)
}
if err := accesses.BulkInsertReportAccesses(
ctx,
tx,
s.svc.scope,
access.ID,
access.OrganizationID,
newReportIDs,
coredata.TrustCenterDocumentAccessStatusRequested,
now,
); err != nil {
return fmt.Errorf("cannot bulk insert trust center report accesses: %w", err)
}
if err := accesses.BulkInsertTrustCenterFileAccesses(
ctx,
tx,
s.svc.scope,
access.ID,
access.OrganizationID,
newTrustCenterFileIDs,
coredata.TrustCenterDocumentAccessStatusRequested,
now,
); err != nil {
return fmt.Errorf("cannot bulk insert trust center file accesses: %w", err)
}
return nil
})
return nil
},
)
if err != nil {
return nil, err