diff --git a/e2e/trust/trust_center_slug_test.go b/e2e/trust/trust_center_slug_test.go new file mode 100644 index 000000000..3efc8152c --- /dev/null +++ b/e2e/trust/trust_center_slug_test.go @@ -0,0 +1,66 @@ +// Copyright (c) 2026 Probo Inc . +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +package trust_test + +import ( + "regexp" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.probo.inc/probo/e2e/internal/testutil" +) + +func TestTrustCenter_SlugHasEntropySuffix(t *testing.T) { + t.Parallel() + + owner := testutil.NewClient(t, testutil.RoleOwner) + organizationID := owner.GetOrganizationID().String() + + const query = ` + query($organizationId: ID!) { + node(id: $organizationId) { + ... on Organization { + trustCenter { + slug + } + } + } + } + ` + + var result struct { + Node struct { + TrustCenter struct { + Slug string `json:"slug"` + } `json:"trustCenter"` + } `json:"node"` + } + + err := owner.Execute(query, map[string]any{ + "organizationId": organizationID, + }, &result) + require.NoError(t, err) + require.NotEmpty(t, result.Node.TrustCenter.Slug) + + slugWithEntropy := regexp.MustCompile(`^[a-z0-9-]+-[0-9a-f]{8}$`) + assert.Regexp(t, slugWithEntropy, result.Node.TrustCenter.Slug) +} diff --git a/pkg/complianceportal/domain_resolver.go b/pkg/complianceportal/domain_resolver.go deleted file mode 100644 index 23e5d4396..000000000 --- a/pkg/complianceportal/domain_resolver.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) 2025-2026 Probo Inc . -// -// 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 complianceportal - -import ( - "context" - - "go.gearno.de/kit/pg" - "go.probo.inc/probo/pkg/complianceportal/resolver" - "go.probo.inc/probo/pkg/coredata" -) - -// EffectiveDomainForTrustCenter returns the domain a compliance page is served -// under: the custom domain when it has an active certificate, otherwise the -// default subdomain when its certificate is active. It returns nil when no -// serving domain is available yet. -// -// It is the single certificate-status-aware domain resolver shared by the -// management and visitor sub-packages. -func EffectiveDomainForTrustCenter( - ctx context.Context, - conn pg.Querier, - scope coredata.Scoper, - trustCenter *coredata.TrustCenter, -) (*coredata.CustomDomain, error) { - return resolver.EffectiveDomainForTrustCenter(ctx, conn, scope, trustCenter) -} - -// PublicURLForTrustCenter returns the canonical public URL of a compliance -// page on its dedicated domain. -func PublicURLForTrustCenter( - ctx context.Context, - conn pg.Querier, - scope coredata.Scoper, - trustCenter *coredata.TrustCenter, - baseDomain string, -) (string, error) { - return resolver.PublicURLForTrustCenter(ctx, conn, scope, trustCenter, baseDomain) -} diff --git a/pkg/complianceportal/management/access_service.go b/pkg/complianceportal/management/access_service.go index ce769d96b..039a85745 100644 --- a/pkg/complianceportal/management/access_service.go +++ b/pkg/complianceportal/management/access_service.go @@ -83,7 +83,7 @@ func (utcar *UpdateAccessRequest) Validate() error { func (s *Service) ListAccesses( ctx context.Context, scope coredata.Scoper, - trustCenterID gid.GID, + compliancePageID gid.GID, cursor *page.Cursor[coredata.TrustCenterAccessOrderField], ) (*page.Page[*coredata.TrustCenterAccess, coredata.TrustCenterAccessOrderField], error) { var accesses coredata.TrustCenterAccesses @@ -91,7 +91,7 @@ func (s *Service) ListAccesses( err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - return accesses.LoadByTrustCenterID(ctx, conn, scope, trustCenterID, cursor) + return accesses.LoadByTrustCenterID(ctx, conn, scope, compliancePageID, cursor) }, ) if err != nil { @@ -244,7 +244,7 @@ func (s *Service) UpdateAccess( access = &coredata.TrustCenterAccess{} if err := access.LoadByID(ctx, tx, scope, req.ID); err != nil { - return fmt.Errorf("cannot load trust center access: %w", err) + return fmt.Errorf("cannot load compliance page access: %w", err) } var tcdas coredata.TrustCenterDocumentAccesses @@ -310,11 +310,11 @@ func (s *Service) UpdateAccess( trustCenterFiles := &coredata.TrustCenterFiles{} if err := trustCenterFiles.LoadByIDs(ctx, tx, scope, trustCenterFileIDs); err != nil { - return fmt.Errorf("cannot load trust center files: %w", err) + return fmt.Errorf("cannot load compliance page files: %w", err) } if err := tcdas.MergeTrustCenterFileAccesses(ctx, tx, scope, access.OrganizationID, access.ID, fileData); err != nil { - return fmt.Errorf("cannot merge trust center file accesses: %w", err) + return fmt.Errorf("cannot merge compliance page file accesses: %w", err) } } @@ -358,11 +358,11 @@ func (s *Service) DeleteAccess( access := &coredata.TrustCenterAccess{} if err := access.LoadByID(ctx, tx, scope, trustCenterAccessID); err != nil { - return fmt.Errorf("cannot load trust center access: %w", err) + return fmt.Errorf("cannot load compliance page access: %w", err) } if err := access.Delete(ctx, tx, scope); err != nil { - return fmt.Errorf("cannot delete trust center access: %w", err) + return fmt.Errorf("cannot delete compliance page access: %w", err) } return nil @@ -387,7 +387,7 @@ func (s *Service) sendAccessEmail( access.UpdatedAt = now if err := access.Update(ctx, tx, scope); err != nil { - return fmt.Errorf("cannot update trust center access with expiration: %w", err) + return fmt.Errorf("cannot update compliance page access with expiration: %w", err) } profile := &coredata.MembershipProfile{} @@ -410,7 +410,7 @@ func (s *Service) sendAccessEmail( subject, textBody, htmlBody, err := emailPresenter.RenderTrustCenterAccess(ctx, organization.Name) if err != nil { - return fmt.Errorf("cannot render trust center access email: %w", err) + return fmt.Errorf("cannot render compliance page access email: %w", err) } accessEmail := coredata.NewEmail( diff --git a/pkg/complianceportal/actions.go b/pkg/complianceportal/management/actions.go similarity index 99% rename from pkg/complianceportal/actions.go rename to pkg/complianceportal/management/actions.go index 1def1d104..7ab4394d8 100644 --- a/pkg/complianceportal/actions.go +++ b/pkg/complianceportal/management/actions.go @@ -12,7 +12,7 @@ // OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. -package complianceportal +package management const ( // Custom domain actions. diff --git a/pkg/complianceportal/management/custom_link_service.go b/pkg/complianceportal/management/custom_link_service.go index e7046d97d..8f0bf70ca 100644 --- a/pkg/complianceportal/management/custom_link_service.go +++ b/pkg/complianceportal/management/custom_link_service.go @@ -78,7 +78,7 @@ func (r *DeleteCustomLinkRequest) Validate() error { func (s *Service) ListCustomLinks( ctx context.Context, scope coredata.Scoper, - trustCenterID gid.GID, + compliancePageID gid.GID, cursor *page.Cursor[coredata.ComplianceCustomLinkOrderField], ) (*page.Page[*coredata.ComplianceCustomLink, coredata.ComplianceCustomLinkOrderField], error) { var items coredata.ComplianceCustomLinks @@ -86,7 +86,7 @@ func (s *Service) ListCustomLinks( err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - if err := items.LoadByTrustCenterID(ctx, conn, scope, trustCenterID, cursor); err != nil { + if err := items.LoadByTrustCenterID(ctx, conn, scope, compliancePageID, cursor); err != nil { return fmt.Errorf("cannot load custom links: %w", err) } @@ -117,14 +117,14 @@ func (s *Service) CreateCustomLink( err := s.pg.WithTx( ctx, func(ctx context.Context, tx pg.Tx) error { - trustCenter := &coredata.TrustCenter{} - if err := trustCenter.LoadByID(ctx, tx, scope, req.TrustCenterID); err != nil { - return fmt.Errorf("cannot load trust center: %w", err) + compliancePage := &coredata.TrustCenter{} + if err := compliancePage.LoadByID(ctx, tx, scope, req.TrustCenterID); err != nil { + return fmt.Errorf("cannot load compliance page: %w", err) } item = &coredata.ComplianceCustomLink{ ID: id, - OrganizationID: trustCenter.OrganizationID, + OrganizationID: compliancePage.OrganizationID, TrustCenterID: req.TrustCenterID, Name: req.Name, URL: req.URL, diff --git a/pkg/complianceportal/resolver/resolver.go b/pkg/complianceportal/management/domain.go similarity index 58% rename from pkg/complianceportal/resolver/resolver.go rename to pkg/complianceportal/management/domain.go index 67b84c4bd..692d065ed 100644 --- a/pkg/complianceportal/resolver/resolver.go +++ b/pkg/complianceportal/management/domain.go @@ -12,7 +12,7 @@ // OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. -package resolver +package management import ( "context" @@ -23,29 +23,25 @@ import ( "go.probo.inc/probo/pkg/gid" ) -// EffectiveDomainForTrustCenter returns the domain a compliance page is served -// under: the custom domain when it has an active certificate, otherwise the -// default subdomain when its certificate is active. It returns nil when no -// serving domain is available yet. -func EffectiveDomainForTrustCenter( +func (s *Service) EffectiveDomainForCompliancePage( ctx context.Context, conn pg.Querier, scope coredata.Scoper, - trustCenter *coredata.TrustCenter, + compliancePage *coredata.TrustCenter, ) (*coredata.CustomDomain, error) { - byID, active, err := loadDomains(ctx, conn, scope, trustCenter) + byID, active, err := loadDomains(ctx, conn, scope, compliancePage) if err != nil { return nil, err } - if trustCenter.CustomDomainID != nil { - if d := byID[*trustCenter.CustomDomainID]; d != nil && active[d.ID] { + if compliancePage.CustomDomainID != nil { + if d := byID[*compliancePage.CustomDomainID]; d != nil && active[d.ID] { return d, nil } } - if trustCenter.DefaultDomainID != nil { - if d := byID[*trustCenter.DefaultDomainID]; d != nil && active[d.ID] { + if compliancePage.DefaultDomainID != nil { + if d := byID[*compliancePage.DefaultDomainID]; d != nil && active[d.ID] { return d, nil } } @@ -53,20 +49,13 @@ func EffectiveDomainForTrustCenter( return nil, nil } -// PublicURLForTrustCenter returns the canonical public URL of a compliance -// page. Compliance pages are always served on a dedicated domain: the custom -// domain when its certificate is active, otherwise the default probopage -// subdomain (even while its certificate provisions), and finally the default -// subdomain hostname derived from the page slug when no domain row is loaded -// yet. -func PublicURLForTrustCenter( +func (s *Service) PublicURLForCompliancePage( ctx context.Context, conn pg.Querier, scope coredata.Scoper, - trustCenter *coredata.TrustCenter, - baseDomain string, + compliancePage *coredata.TrustCenter, ) (string, error) { - byID, active, err := loadDomains(ctx, conn, scope, trustCenter) + byID, active, err := loadDomains(ctx, conn, scope, compliancePage) if err != nil { return "", err } @@ -74,14 +63,14 @@ func PublicURLForTrustCenter( var host string switch { - case trustCenter.CustomDomainID != nil && byID[*trustCenter.CustomDomainID] != nil && active[*trustCenter.CustomDomainID]: - host = byID[*trustCenter.CustomDomainID].Domain - case trustCenter.DefaultDomainID != nil && byID[*trustCenter.DefaultDomainID] != nil: - host = byID[*trustCenter.DefaultDomainID].Domain + case compliancePage.CustomDomainID != nil && byID[*compliancePage.CustomDomainID] != nil && active[*compliancePage.CustomDomainID]: + host = byID[*compliancePage.CustomDomainID].Domain + case compliancePage.DefaultDomainID != nil && byID[*compliancePage.DefaultDomainID] != nil: + host = byID[*compliancePage.DefaultDomainID].Domain } if host == "" { - host = trustCenter.Slug + "." + baseDomain + host = compliancePage.Slug + "." + s.baseDomain } return "https://" + host, nil @@ -91,15 +80,15 @@ func loadDomains( ctx context.Context, conn pg.Querier, scope coredata.Scoper, - trustCenter *coredata.TrustCenter, + compliancePage *coredata.TrustCenter, ) (map[gid.GID]*coredata.CustomDomain, map[gid.GID]bool, error) { var ids []gid.GID - if trustCenter.CustomDomainID != nil { - ids = append(ids, *trustCenter.CustomDomainID) + if compliancePage.CustomDomainID != nil { + ids = append(ids, *compliancePage.CustomDomainID) } - if trustCenter.DefaultDomainID != nil { - ids = append(ids, *trustCenter.DefaultDomainID) + if compliancePage.DefaultDomainID != nil { + ids = append(ids, *compliancePage.DefaultDomainID) } byID := make(map[gid.GID]*coredata.CustomDomain) diff --git a/pkg/complianceportal/management/domain_service.go b/pkg/complianceportal/management/domain_service.go index 6ca8d0309..b5b79a49c 100644 --- a/pkg/complianceportal/management/domain_service.go +++ b/pkg/complianceportal/management/domain_service.go @@ -21,27 +21,13 @@ import ( "time" "go.gearno.de/kit/pg" - "go.probo.inc/probo/pkg/complianceportal" "go.probo.inc/probo/pkg/coredata" "go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/validator" ) -// The compliance portal service owns the relationship between a compliance -// page (trust center) and its domains. A page has two slots stored on the -// trust center row: a default {slug}.probopage.com domain provided by Probo and -// an optional custom domain. It provisions each domain's TLS certificate -// through the generic certmanager service within the trust center's transaction -// so slot changes stay atomic with the page. - -// ErrCustomDomainSlotTaken is returned when a compliance page already has a -// custom domain and another one is added. var ErrCustomDomainSlotTaken = errors.New("compliance page already has a custom domain") -// AddCustomDomain provisions the compliance page's custom domain. It fails -// when the page already has one. The default probopage subdomain, provisioned -// at page creation, keeps serving as a fallback while the new certificate -// provisions. func (s *Service) AddCustomDomain( ctx context.Context, scope coredata.Scoper, @@ -60,12 +46,12 @@ func (s *Service) AddCustomDomain( err := s.pg.WithTx( ctx, func(ctx context.Context, tx pg.Tx) error { - trustCenter := &coredata.TrustCenter{} - if err := trustCenter.LoadByID(ctx, tx, scope, compliancePageID); err != nil { - return fmt.Errorf("cannot load trust center: %w", err) + compliancePage := &coredata.TrustCenter{} + if err := compliancePage.LoadByID(ctx, tx, scope, compliancePageID); err != nil { + return fmt.Errorf("cannot load compliance page: %w", err) } - if trustCenter.CustomDomainID != nil { + if compliancePage.CustomDomainID != nil { return ErrCustomDomainSlotTaken } @@ -76,7 +62,7 @@ func (s *Service) AddCustomDomain( customDomain = coredata.NewCustomDomain( scope.GetTenantID(), - trustCenter.OrganizationID, + compliancePage.OrganizationID, domain, false, ) @@ -86,11 +72,11 @@ func (s *Service) AddCustomDomain( return fmt.Errorf("cannot insert custom domain: %w", err) } - trustCenter.CustomDomainID = &customDomain.ID - trustCenter.UpdatedAt = time.Now() + compliancePage.CustomDomainID = &customDomain.ID + compliancePage.UpdatedAt = time.Now() - if err := trustCenter.Update(ctx, tx, scope); err != nil { - return fmt.Errorf("cannot update trust center: %w", err) + if err := compliancePage.Update(ctx, tx, scope); err != nil { + return fmt.Errorf("cannot update compliance page: %w", err) } return nil @@ -103,9 +89,6 @@ func (s *Service) AddCustomDomain( return customDomain, nil } -// RemoveCustomDomain clears the compliance page's custom domain and deletes -// the underlying domain together with its certificate. The default domain -// cannot be removed. func (s *Service) RemoveCustomDomain( ctx context.Context, scope coredata.Scoper, @@ -120,24 +103,24 @@ func (s *Service) RemoveCustomDomain( } if domain.Managed { - return complianceportal.ErrCustomDomainManaged + return ErrCustomDomainManaged } - trustCenter := &coredata.TrustCenter{} - err := trustCenter.LoadByDomainID(ctx, tx, customDomainID) + compliancePage := &coredata.TrustCenter{} + err := compliancePage.LoadByDomainID(ctx, tx, customDomainID) switch { case err == nil: - if trustCenter.CustomDomainID != nil && *trustCenter.CustomDomainID == customDomainID { - trustCenter.CustomDomainID = nil - trustCenter.UpdatedAt = time.Now() + if compliancePage.CustomDomainID != nil && *compliancePage.CustomDomainID == customDomainID { + compliancePage.CustomDomainID = nil + compliancePage.UpdatedAt = time.Now() - if err := trustCenter.Update(ctx, tx, scope); err != nil { - return fmt.Errorf("cannot update trust center: %w", err) + if err := compliancePage.Update(ctx, tx, scope); err != nil { + return fmt.Errorf("cannot update compliance page: %w", err) } } case errors.Is(err, coredata.ErrResourceNotFound): default: - return fmt.Errorf("cannot load trust center by domain id: %w", err) + return fmt.Errorf("cannot load compliance page by domain id: %w", err) } if err := domain.Delete(ctx, tx, scope); err != nil { @@ -155,78 +138,37 @@ func (s *Service) RemoveCustomDomain( ) } -// GetCertificate returns the certificate backing a custom domain, or nil when -// the domain has no certificate yet. -func (s *Service) GetCertificate( - ctx context.Context, - scope coredata.Scoper, - domain *coredata.CustomDomain, -) (*coredata.Certificate, error) { - if domain == nil || domain.CertificateID == nil { - return nil, nil - } - - return s.certManager.Get(ctx, scope, *domain.CertificateID) -} - // GetDefaultDomain returns the compliance page's default probopage subdomain, // or nil when it has not been provisioned yet. func (s *Service) GetDefaultDomain( ctx context.Context, scope coredata.Scoper, compliancePageID gid.GID, -) (*coredata.CustomDomain, error) { - return s.domainSlot(ctx, scope, compliancePageID, func(tc *coredata.TrustCenter) *gid.GID { - return tc.DefaultDomainID - }, - ) -} - -// GetCustomDomain returns the compliance page's custom domain, or nil when -// none is configured. -func (s *Service) GetCustomDomain( - ctx context.Context, - scope coredata.Scoper, - compliancePageID gid.GID, -) (*coredata.CustomDomain, error) { - return s.domainSlot(ctx, scope, compliancePageID, func(tc *coredata.TrustCenter) *gid.GID { - return tc.CustomDomainID - }, - ) -} - -func (s *Service) domainSlot( - ctx context.Context, - scope coredata.Scoper, - compliancePageID gid.GID, - slot func(*coredata.TrustCenter) *gid.GID, ) (*coredata.CustomDomain, error) { var domain *coredata.CustomDomain err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - trustCenter := &coredata.TrustCenter{} - if err := trustCenter.LoadByID(ctx, conn, scope, compliancePageID); err != nil { - return fmt.Errorf("cannot load trust center: %w", err) + compliancePage := &coredata.TrustCenter{} + if err := compliancePage.LoadByID(ctx, conn, scope, compliancePageID); err != nil { + return fmt.Errorf("cannot load compliance page: %w", err) } - domainID := slot(trustCenter) - if domainID == nil { + if compliancePage.DefaultDomainID == nil { return nil } - loaded := &coredata.CustomDomain{} - if err := loaded.LoadByID(ctx, conn, scope, *domainID); err != nil { + domain = &coredata.CustomDomain{} + if err := domain.LoadByID(ctx, conn, scope, *compliancePage.DefaultDomainID); err != nil { if errors.Is(err, coredata.ErrResourceNotFound) { + domain = nil return nil } return fmt.Errorf("cannot load custom domain: %w", err) } - domain = loaded - return nil }, ) @@ -237,31 +179,34 @@ func (s *Service) domainSlot( return domain, nil } -// EffectiveDomain returns the domain a compliance page is served under: the -// custom domain when it has an active certificate, otherwise the default -// subdomain when its certificate is active. It returns nil when no serving -// domain is available yet. -func (s *Service) EffectiveDomain( +func (s *Service) GetCustomDomain( ctx context.Context, scope coredata.Scoper, compliancePageID gid.GID, ) (*coredata.CustomDomain, error) { - var effective *coredata.CustomDomain + var domain *coredata.CustomDomain err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - trustCenter := &coredata.TrustCenter{} - if err := trustCenter.LoadByID(ctx, conn, scope, compliancePageID); err != nil { - return fmt.Errorf("cannot load trust center: %w", err) + compliancePage := &coredata.TrustCenter{} + if err := compliancePage.LoadByID(ctx, conn, scope, compliancePageID); err != nil { + return fmt.Errorf("cannot load compliance page: %w", err) } - d, err := complianceportal.EffectiveDomainForTrustCenter(ctx, conn, scope, trustCenter) - if err != nil { - return err + if compliancePage.CustomDomainID == nil { + return nil } - effective = d + domain = &coredata.CustomDomain{} + if err := domain.LoadByID(ctx, conn, scope, *compliancePage.CustomDomainID); err != nil { + if errors.Is(err, coredata.ErrResourceNotFound) { + domain = nil + return nil + } + + return fmt.Errorf("cannot load custom domain: %w", err) + } return nil }, @@ -270,26 +215,7 @@ func (s *Service) EffectiveDomain( return nil, err } - return effective, nil -} - -// EffectiveCanonicalHost returns the host a compliance page should be served -// under, or an empty string when no serving host is available yet. -func (s *Service) EffectiveCanonicalHost( - ctx context.Context, - scope coredata.Scoper, - compliancePageID gid.GID, -) (string, error) { - domain, err := s.EffectiveDomain(ctx, scope, compliancePageID) - if err != nil { - return "", err - } - - if domain == nil { - return "", nil - } - - return domain.Domain, nil + return domain, nil } // PublicURL returns the canonical public URL of a compliance page on its @@ -304,18 +230,17 @@ func (s *Service) PublicURL( err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - trustCenter := &coredata.TrustCenter{} - if err := trustCenter.LoadByID(ctx, conn, scope, compliancePageID); err != nil { - return fmt.Errorf("cannot load trust center: %w", err) + compliancePage := &coredata.TrustCenter{} + if err := compliancePage.LoadByID(ctx, conn, scope, compliancePageID); err != nil { + return fmt.Errorf("cannot load compliance page: %w", err) } - url, err := complianceportal.PublicURLForTrustCenter(ctx, conn, scope, trustCenter, s.baseDomain) + var err error + publicURL, err = s.PublicURLForCompliancePage(ctx, conn, scope, compliancePage) if err != nil { - return err + return fmt.Errorf("cannot resolve public url: %w", err) } - publicURL = url - return nil }, ) diff --git a/pkg/complianceportal/errors.go b/pkg/complianceportal/management/errors.go similarity index 98% rename from pkg/complianceportal/errors.go rename to pkg/complianceportal/management/errors.go index 6b6cc01d7..7319cad85 100644 --- a/pkg/complianceportal/errors.go +++ b/pkg/complianceportal/management/errors.go @@ -12,7 +12,7 @@ // OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. -package complianceportal +package management import "errors" diff --git a/pkg/complianceportal/management/file_service.go b/pkg/complianceportal/management/file_service.go index d2ff8eb70..0679df7d6 100644 --- a/pkg/complianceportal/management/file_service.go +++ b/pkg/complianceportal/management/file_service.go @@ -92,7 +92,7 @@ func (s *Service) ListFilesForOrganizationID( ctx, func(ctx context.Context, conn pg.Querier) error { if err := files.LoadByOrganizationID(ctx, conn, scope, organizationID, cursor, filter); err != nil { - return fmt.Errorf("cannot load trust center files: %w", err) + return fmt.Errorf("cannot load compliance page files: %w", err) } return nil @@ -119,7 +119,7 @@ func (s *Service) CountFilesForOrganizationID( count, err = (&coredata.TrustCenterFiles{}).CountByOrganizationID(ctx, conn, scope, organizationID) if err != nil { - return fmt.Errorf("cannot count trust center files: %w", err) + return fmt.Errorf("cannot count compliance page files: %w", err) } return nil @@ -144,7 +144,7 @@ func (s *Service) GetFile( func(ctx context.Context, conn pg.Querier) error { file = &coredata.TrustCenterFile{} if err := file.LoadByID(ctx, conn, scope, id); err != nil { - return fmt.Errorf("cannot load trust center file: %w", err) + return fmt.Errorf("cannot load compliance page file: %w", err) } return nil @@ -210,7 +210,7 @@ func (s *Service) CreateFile( } if err := file.Insert(ctx, tx, scope); err != nil { - return fmt.Errorf("cannot insert trust center file: %w", err) + return fmt.Errorf("cannot insert compliance page file: %w", err) } return nil @@ -243,7 +243,7 @@ func (s *Service) UpdateFile( file = &coredata.TrustCenterFile{} if err := file.LoadByID(ctx, tx, scope, req.ID); err != nil { - return fmt.Errorf("cannot load trust center file: %w", err) + return fmt.Errorf("cannot load compliance page file: %w", err) } if req.Name != nil { @@ -261,7 +261,7 @@ func (s *Service) UpdateFile( file.UpdatedAt = now if err := file.Update(ctx, tx, scope); err != nil { - return fmt.Errorf("cannot update trust center file: %w", err) + return fmt.Errorf("cannot update compliance page file: %w", err) } return nil @@ -285,11 +285,11 @@ func (s *Service) DeleteFile( file := &coredata.TrustCenterFile{} if err := file.LoadByID(ctx, tx, scope, trustCenterFileID); err != nil { - return fmt.Errorf("cannot load trust center file: %w", err) + return fmt.Errorf("cannot load compliance page file: %w", err) } if err := file.Delete(ctx, tx, scope); err != nil { - return fmt.Errorf("cannot delete trust center file: %w", err) + return fmt.Errorf("cannot delete compliance page file: %w", err) } return nil @@ -311,7 +311,7 @@ func (s *Service) GenerateFileURL( func(ctx context.Context, conn pg.Querier) error { file := &coredata.TrustCenterFile{} if err := file.LoadByID(ctx, conn, scope, trustCenterFileID); err != nil { - return fmt.Errorf("cannot load trust center file: %w", err) + return fmt.Errorf("cannot load compliance page file: %w", err) } storedFile = &coredata.File{} @@ -405,9 +405,9 @@ func (s *Service) uploadFile( ContentType: new(contentType), CacheControl: new("private, max-age=3600"), Metadata: map[string]string{ - "type": "trust-center-file", - "trust-center-file-id": trustCenterFileID.String(), - "organization-id": organizationID.String(), + "type": "compliance-page-file", + "compliance-page-file-id": trustCenterFileID.String(), + "organization-id": organizationID.String(), }, }, ) diff --git a/pkg/complianceportal/management/framework_service.go b/pkg/complianceportal/management/framework_service.go index bcd5dc50d..4b5c5c1dc 100644 --- a/pkg/complianceportal/management/framework_service.go +++ b/pkg/complianceportal/management/framework_service.go @@ -76,7 +76,7 @@ func (r *DeleteFrameworkRequest) Validate() error { func (s *Service) ListFrameworksWithHidden( ctx context.Context, scope coredata.Scoper, - trustCenterID gid.GID, + compliancePageID gid.GID, cursor *page.Cursor[coredata.ComplianceFrameworkOrderField], ) (*page.Page[*coredata.ComplianceFramework, coredata.ComplianceFrameworkOrderField], error) { var cfs coredata.ComplianceFrameworks @@ -84,7 +84,7 @@ func (s *Service) ListFrameworksWithHidden( err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - if err := cfs.LoadWithHiddenByTrustCenterID(ctx, conn, scope, trustCenterID, cursor); err != nil { + if err := cfs.LoadWithHiddenByTrustCenterID(ctx, conn, scope, compliancePageID, cursor); err != nil { return fmt.Errorf("cannot load frameworks with hidden: %w", err) } @@ -116,9 +116,9 @@ func (s *Service) CreateFramework( err := s.pg.WithTx( ctx, func(ctx context.Context, tx pg.Tx) error { - trustCenter := &coredata.TrustCenter{} - if err := trustCenter.LoadByID(ctx, tx, scope, req.TrustCenterID); err != nil { - return fmt.Errorf("cannot load trust center: %w", err) + compliancePage := &coredata.TrustCenter{} + if err := compliancePage.LoadByID(ctx, tx, scope, req.TrustCenterID); err != nil { + return fmt.Errorf("cannot load compliance page: %w", err) } framework := &coredata.Framework{} @@ -128,7 +128,7 @@ func (s *Service) CreateFramework( cf = &coredata.ComplianceFramework{ ID: cfID, - OrganizationID: trustCenter.OrganizationID, + OrganizationID: compliancePage.OrganizationID, TrustCenterID: req.TrustCenterID, FrameworkID: req.FrameworkID, CreatedAt: now, diff --git a/pkg/complianceportal/oauth2_scopes.go b/pkg/complianceportal/management/oauth2_scopes.go similarity index 99% rename from pkg/complianceportal/oauth2_scopes.go rename to pkg/complianceportal/management/oauth2_scopes.go index c2e86058f..7e041fa0e 100644 --- a/pkg/complianceportal/oauth2_scopes.go +++ b/pkg/complianceportal/management/oauth2_scopes.go @@ -12,7 +12,7 @@ // OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. -package complianceportal +package management import ( "go.probo.inc/probo/pkg/coredata" diff --git a/pkg/complianceportal/policies.go b/pkg/complianceportal/management/policies.go similarity index 99% rename from pkg/complianceportal/policies.go rename to pkg/complianceportal/management/policies.go index 658064958..2c3908ec6 100644 --- a/pkg/complianceportal/policies.go +++ b/pkg/complianceportal/management/policies.go @@ -12,7 +12,7 @@ // OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. -package complianceportal +package management import ( "go.probo.inc/probo/pkg/iam" diff --git a/pkg/complianceportal/management/portal_service.go b/pkg/complianceportal/management/portal_service.go index ae0b24751..ce5eeb441 100644 --- a/pkg/complianceportal/management/portal_service.go +++ b/pkg/complianceportal/management/portal_service.go @@ -33,7 +33,6 @@ import ( "go.gearno.de/crypto/uuid" "go.gearno.de/kit/pg" "go.probo.inc/probo/packages/emails" - "go.probo.inc/probo/pkg/complianceportal" "go.probo.inc/probo/pkg/coredata" "go.probo.inc/probo/pkg/filevalidation" "go.probo.inc/probo/pkg/gid" @@ -134,26 +133,26 @@ func (req *UpdateBrandRequest) Validate() error { func (s *Service) Get( ctx context.Context, scope coredata.Scoper, - trustCenterID gid.GID, + compliancePageID gid.GID, ) (*coredata.TrustCenter, error) { - var trustCenter *coredata.TrustCenter + var compliancePage *coredata.TrustCenter err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - trustCenter = &coredata.TrustCenter{} - if err := trustCenter.LoadByID(ctx, conn, scope, trustCenterID); err != nil { - return fmt.Errorf("cannot load trust center: %w", err) + compliancePage = &coredata.TrustCenter{} + if err := compliancePage.LoadByID(ctx, conn, scope, compliancePageID); err != nil { + return fmt.Errorf("cannot load compliance page: %w", err) } return nil }, ) if err != nil { - return nil, fmt.Errorf("cannot load trust center: %w", err) + return nil, fmt.Errorf("cannot load compliance page: %w", err) } - return trustCenter, nil + return compliancePage, nil } func (s *Service) GetByOrganizationID( @@ -161,14 +160,14 @@ func (s *Service) GetByOrganizationID( scope coredata.Scoper, organizationID gid.GID, ) (*coredata.TrustCenter, error) { - var trustCenter *coredata.TrustCenter + var compliancePage *coredata.TrustCenter err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - trustCenter = &coredata.TrustCenter{} - if err := trustCenter.LoadByOrganizationID(ctx, conn, scope, organizationID); err != nil { - return fmt.Errorf("cannot load trust center: %w", err) + compliancePage = &coredata.TrustCenter{} + if err := compliancePage.LoadByOrganizationID(ctx, conn, scope, organizationID); err != nil { + return fmt.Errorf("cannot load compliance page: %w", err) } return nil @@ -178,7 +177,7 @@ func (s *Service) GetByOrganizationID( return nil, err } - return trustCenter, nil + return compliancePage, nil } func (s *Service) Update( @@ -191,40 +190,40 @@ func (s *Service) Update( } var ( - trustCenter *coredata.TrustCenter - file *coredata.File + compliancePage *coredata.TrustCenter + file *coredata.File ) err := s.pg.WithTx( ctx, func(ctx context.Context, conn pg.Tx) error { - trustCenter = &coredata.TrustCenter{} - if err := trustCenter.LoadByID(ctx, conn, scope, req.ID); err != nil { - return fmt.Errorf("cannot load trust center: %w", err) + compliancePage = &coredata.TrustCenter{} + if err := compliancePage.LoadByID(ctx, conn, scope, req.ID); err != nil { + return fmt.Errorf("cannot load compliance page: %w", err) } if req.Active != nil { - trustCenter.Active = *req.Active + compliancePage.Active = *req.Active } if req.Slug != nil { - trustCenter.Slug = *req.Slug + compliancePage.Slug = *req.Slug } if req.SearchEngineIndexing != nil { - trustCenter.SearchEngineIndexing = *req.SearchEngineIndexing + compliancePage.SearchEngineIndexing = *req.SearchEngineIndexing } if req.Title != nil { - trustCenter.Title = *req.Title + compliancePage.Title = *req.Title } if req.Description != nil { - trustCenter.Description = *req.Description + compliancePage.Description = *req.Description } if req.WebsiteURL != nil { - trustCenter.WebsiteURL = *req.WebsiteURL + compliancePage.WebsiteURL = *req.WebsiteURL } if req.Email != nil { @@ -234,22 +233,22 @@ func (s *Service) Update( } } - trustCenter.Email = *req.Email + compliancePage.Email = *req.Email } if req.HeadquarterAddress != nil { - trustCenter.HeadquarterAddress = *req.HeadquarterAddress + compliancePage.HeadquarterAddress = *req.HeadquarterAddress } - trustCenter.UpdatedAt = time.Now() + compliancePage.UpdatedAt = time.Now() - if err := trustCenter.Update(ctx, conn, scope); err != nil { - return fmt.Errorf("cannot update trust center: %w", err) + if err := compliancePage.Update(ctx, conn, scope); err != nil { + return fmt.Errorf("cannot update compliance page: %w", err) } - if trustCenter.NonDisclosureAgreementFileID != nil { + if compliancePage.NonDisclosureAgreementFileID != nil { file = &coredata.File{} - if err := file.LoadByID(ctx, conn, scope, *trustCenter.NonDisclosureAgreementFileID); err != nil { + if err := file.LoadByID(ctx, conn, scope, *compliancePage.NonDisclosureAgreementFileID); err != nil { return fmt.Errorf("cannot load file: %w", err) } } @@ -261,7 +260,7 @@ func (s *Service) Update( return nil, nil, err } - return trustCenter, file, nil + return compliancePage, file, nil } func (s *Service) UploadNDA( @@ -274,20 +273,20 @@ func (s *Service) UploadNDA( } var ( - trustCenter *coredata.TrustCenter - file *coredata.File + compliancePage *coredata.TrustCenter + file *coredata.File ) err := s.pg.WithTx( ctx, func(ctx context.Context, conn pg.Tx) error { - trustCenter = &coredata.TrustCenter{} - if err := trustCenter.LoadByID(ctx, conn, scope, req.TrustCenterID); err != nil { - return fmt.Errorf("cannot load trust center: %w", err) + compliancePage = &coredata.TrustCenter{} + if err := compliancePage.LoadByID(ctx, conn, scope, req.TrustCenterID); err != nil { + return fmt.Errorf("cannot load compliance page: %w", err) } - if trustCenter.OrganizationID == gid.Nil { - return fmt.Errorf("trust center %s has no organization", req.TrustCenterID) + if compliancePage.OrganizationID == gid.Nil { + return fmt.Errorf("compliance page %s has no organization", req.TrustCenterID) } objectKey, err := uuid.NewV7() @@ -305,7 +304,7 @@ func (s *Service) UploadNDA( file = &coredata.File{ ID: fileID, - OrganizationID: trustCenter.OrganizationID, + OrganizationID: compliancePage.OrganizationID, BucketName: s.bucket, MimeType: mimeType, FileName: req.FileName, @@ -320,9 +319,9 @@ func (s *Service) UploadNDA( file, req.File, map[string]string{ - "type": "trust-center-nda", - "trust-center-id": req.TrustCenterID.String(), - "organization-id": trustCenter.OrganizationID.String(), + "type": "compliance-page-nda", + "compliance-page-id": req.TrustCenterID.String(), + "organization-id": compliancePage.OrganizationID.String(), }, ) if err != nil { @@ -335,11 +334,11 @@ func (s *Service) UploadNDA( return fmt.Errorf("cannot insert file: %w", err) } - trustCenter.NonDisclosureAgreementFileID = &fileID - trustCenter.UpdatedAt = now + compliancePage.NonDisclosureAgreementFileID = &fileID + compliancePage.UpdatedAt = now - if err := trustCenter.Update(ctx, conn, scope); err != nil { - return fmt.Errorf("cannot update trust center: %w", err) + if err := compliancePage.Update(ctx, conn, scope); err != nil { + return fmt.Errorf("cannot update compliance page: %w", err) } return nil @@ -349,29 +348,29 @@ func (s *Service) UploadNDA( return nil, nil, err } - return trustCenter, file, nil + return compliancePage, file, nil } func (s *Service) DeleteNDA( ctx context.Context, scope coredata.Scoper, - trustCenterID gid.GID, + compliancePageID gid.GID, ) (*coredata.TrustCenter, *coredata.File, error) { - var trustCenter *coredata.TrustCenter + var compliancePage *coredata.TrustCenter err := s.pg.WithTx( ctx, func(ctx context.Context, conn pg.Tx) error { - trustCenter = &coredata.TrustCenter{} - if err := trustCenter.LoadByID(ctx, conn, scope, trustCenterID); err != nil { - return fmt.Errorf("cannot load trust center: %w", err) + compliancePage = &coredata.TrustCenter{} + if err := compliancePage.LoadByID(ctx, conn, scope, compliancePageID); err != nil { + return fmt.Errorf("cannot load compliance page: %w", err) } - trustCenter.NonDisclosureAgreementFileID = nil - trustCenter.UpdatedAt = time.Now() + compliancePage.NonDisclosureAgreementFileID = nil + compliancePage.UpdatedAt = time.Now() - if err := trustCenter.Update(ctx, conn, scope); err != nil { - return fmt.Errorf("cannot update trust center: %w", err) + if err := compliancePage.Update(ctx, conn, scope); err != nil { + return fmt.Errorf("cannot update compliance page: %w", err) } return nil @@ -381,7 +380,7 @@ func (s *Service) DeleteNDA( return nil, nil, err } - return trustCenter, nil, nil + return compliancePage, nil, nil } func (s *Service) UpdateBrand( @@ -394,55 +393,55 @@ func (s *Service) UpdateBrand( } var ( - trustCenter *coredata.TrustCenter - ndaFile *coredata.File + compliancePage *coredata.TrustCenter + ndaFile *coredata.File ) err := s.pg.WithTx( ctx, func(ctx context.Context, conn pg.Tx) error { - trustCenter = &coredata.TrustCenter{} - if err := trustCenter.LoadByID(ctx, conn, scope, req.TrustCenterID); err != nil { - return fmt.Errorf("cannot load trust center: %w", err) + compliancePage = &coredata.TrustCenter{} + if err := compliancePage.LoadByID(ctx, conn, scope, req.TrustCenterID); err != nil { + return fmt.Errorf("cannot load compliance page: %w", err) } now := time.Now() if req.LogoFile != nil { if *req.LogoFile == nil { - trustCenter.LogoFileID = nil + compliancePage.LogoFileID = nil } else { - file, err := s.uploadBrandFile(ctx, scope, conn, *req.LogoFile, "trust-center-logo", trustCenter) + file, err := s.uploadBrandFile(ctx, scope, conn, *req.LogoFile, "compliance-page-logo", compliancePage) if err != nil { return fmt.Errorf("cannot upload logo file: %w", err) } - trustCenter.LogoFileID = &file.ID + compliancePage.LogoFileID = &file.ID } } if req.DarkLogoFile != nil { if *req.DarkLogoFile == nil { - trustCenter.DarkLogoFileID = nil + compliancePage.DarkLogoFileID = nil } else { - file, err := s.uploadBrandFile(ctx, scope, conn, *req.DarkLogoFile, "trust-center-dark-logo", trustCenter) + file, err := s.uploadBrandFile(ctx, scope, conn, *req.DarkLogoFile, "compliance-page-dark-logo", compliancePage) if err != nil { return fmt.Errorf("cannot upload dark logo file: %w", err) } - trustCenter.DarkLogoFileID = &file.ID + compliancePage.DarkLogoFileID = &file.ID } } - trustCenter.UpdatedAt = now + compliancePage.UpdatedAt = now - if err := trustCenter.Update(ctx, conn, scope); err != nil { - return fmt.Errorf("cannot update trust center: %w", err) + if err := compliancePage.Update(ctx, conn, scope); err != nil { + return fmt.Errorf("cannot update compliance page: %w", err) } - if trustCenter.NonDisclosureAgreementFileID != nil { + if compliancePage.NonDisclosureAgreementFileID != nil { ndaFile = &coredata.File{} - if err := ndaFile.LoadByID(ctx, conn, scope, *trustCenter.NonDisclosureAgreementFileID); err != nil { + if err := ndaFile.LoadByID(ctx, conn, scope, *compliancePage.NonDisclosureAgreementFileID); err != nil { return fmt.Errorf("cannot load nda file: %w", err) } } @@ -454,7 +453,7 @@ func (s *Service) UpdateBrand( return nil, nil, err } - return trustCenter, ndaFile, nil + return compliancePage, ndaFile, nil } func (s *Service) uploadBrandFile( @@ -463,7 +462,7 @@ func (s *Service) uploadBrandFile( conn pg.Tx, fileUpload *FileUpload, fileType string, - trustCenter *coredata.TrustCenter, + compliancePage *coredata.TrustCenter, ) (*coredata.File, error) { objectKey, err := uuid.NewV7() if err != nil { @@ -482,9 +481,9 @@ func (s *Service) uploadBrandFile( ContentType: &mimeType, CacheControl: new("max-age=3600, public"), Metadata: map[string]string{ - "type": fileType, - "trust-center-id": trustCenter.ID.String(), - "organization-id": trustCenter.OrganizationID.String(), + "type": fileType, + "compliance-page-id": compliancePage.ID.String(), + "organization-id": compliancePage.OrganizationID.String(), }, }) if err != nil { @@ -504,7 +503,7 @@ func (s *Service) uploadBrandFile( file := &coredata.File{ ID: fileID, - OrganizationID: trustCenter.OrganizationID, + OrganizationID: compliancePage.OrganizationID, BucketName: s.bucket, MimeType: mimeType, FileName: fileUpload.Filename, @@ -525,26 +524,26 @@ func (s *Service) uploadBrandFile( func (s *Service) GenerateNDAFileURL( ctx context.Context, scope coredata.Scoper, - trustCenterID gid.GID, + compliancePageID gid.GID, expiresIn time.Duration, ) (*string, error) { var file *coredata.File - trustCenter := &coredata.TrustCenter{} + compliancePage := &coredata.TrustCenter{} err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - if err := trustCenter.LoadByID(ctx, conn, scope, trustCenterID); err != nil { - return fmt.Errorf("cannot load trust center: %w", err) + if err := compliancePage.LoadByID(ctx, conn, scope, compliancePageID); err != nil { + return fmt.Errorf("cannot load compliance page: %w", err) } - if trustCenter.NonDisclosureAgreementFileID == nil { + if compliancePage.NonDisclosureAgreementFileID == nil { return nil } file = &coredata.File{} - if err := file.LoadByID(ctx, conn, scope, *trustCenter.NonDisclosureAgreementFileID); err != nil { + if err := file.LoadByID(ctx, conn, scope, *compliancePage.NonDisclosureAgreementFileID); err != nil { return fmt.Errorf("cannot load file: %w", err) } @@ -555,7 +554,7 @@ func (s *Service) GenerateNDAFileURL( return nil, err } - if trustCenter.NonDisclosureAgreementFileID == nil { + if compliancePage.NonDisclosureAgreementFileID == nil { return nil, nil } @@ -691,13 +690,7 @@ func (s *Service) EmailPresenterConfig( return fmt.Errorf("cannot load organization: %w", err) } - publicURL, err := complianceportal.PublicURLForTrustCenter( - ctx, - conn, - scope, - compliancePage, - s.baseDomain, - ) + publicURL, err := s.PublicURLForCompliancePage(ctx, conn, scope, compliancePage) if err != nil { return fmt.Errorf("cannot resolve compliance page URL: %w", err) } @@ -736,24 +729,24 @@ func (s *Service) EmailPresenterConfig( func (s *Service) GetMailingList( ctx context.Context, scope coredata.Scoper, - trustCenterID gid.GID, + compliancePageID gid.GID, ) (*coredata.MailingList, error) { var mailingList *coredata.MailingList err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - trustCenter := &coredata.TrustCenter{} - if err := trustCenter.LoadByID(ctx, conn, scope, trustCenterID); err != nil { - return fmt.Errorf("cannot load trust center: %w", err) + compliancePage := &coredata.TrustCenter{} + if err := compliancePage.LoadByID(ctx, conn, scope, compliancePageID); err != nil { + return fmt.Errorf("cannot load compliance page: %w", err) } - if trustCenter.MailingListID == nil { + if compliancePage.MailingListID == nil { return nil } mailingList = &coredata.MailingList{} - if err := mailingList.LoadByID(ctx, conn, scope, *trustCenter.MailingListID); err != nil { + if err := mailingList.LoadByID(ctx, conn, scope, *compliancePage.MailingListID); err != nil { return fmt.Errorf("cannot load mailing list: %w", err) } diff --git a/pkg/complianceportal/management/reference_service.go b/pkg/complianceportal/management/reference_service.go index 1ebdd1605..25ffa3308 100644 --- a/pkg/complianceportal/management/reference_service.go +++ b/pkg/complianceportal/management/reference_service.go @@ -82,7 +82,7 @@ func (utcrr *UpdateReferenceRequest) Validate() error { func (s *Service) ListReferences( ctx context.Context, scope coredata.Scoper, - trustCenterID gid.GID, + compliancePageID gid.GID, cursor *page.Cursor[coredata.TrustCenterReferenceOrderField], ) (*page.Page[*coredata.TrustCenterReference, coredata.TrustCenterReferenceOrderField], error) { var references coredata.TrustCenterReferences @@ -90,9 +90,9 @@ func (s *Service) ListReferences( err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - err := references.LoadByTrustCenterID(ctx, conn, scope, trustCenterID, cursor) + err := references.LoadByTrustCenterID(ctx, conn, scope, compliancePageID, cursor) if err != nil { - return fmt.Errorf("cannot load trust center references: %w", err) + return fmt.Errorf("cannot load compliance page references: %w", err) } return nil @@ -108,7 +108,7 @@ func (s *Service) ListReferences( func (s *Service) CountReferences( ctx context.Context, scope coredata.Scoper, - trustCenterID gid.GID, + compliancePageID gid.GID, ) (int, error) { var count int @@ -117,9 +117,9 @@ func (s *Service) CountReferences( func(ctx context.Context, conn pg.Querier) (err error) { references := coredata.TrustCenterReferences{} - count, err = references.CountByTrustCenterID(ctx, conn, scope, trustCenterID) + count, err = references.CountByTrustCenterID(ctx, conn, scope, compliancePageID) if err != nil { - return fmt.Errorf("cannot count trust center references: %w", err) + return fmt.Errorf("cannot count compliance page references: %w", err) } return nil @@ -144,7 +144,7 @@ func (s *Service) GetReference( func(ctx context.Context, conn pg.Querier) error { err := reference.LoadByID(ctx, conn, scope, referenceID) if err != nil { - return fmt.Errorf("cannot load trust center reference: %w", err) + return fmt.Errorf("cannot load compliance page reference: %w", err) } return nil @@ -177,9 +177,9 @@ func (s *Service) CreateReference( err := s.pg.WithTx( ctx, func(ctx context.Context, tx pg.Tx) error { - trustCenter := &coredata.TrustCenter{} - if err := trustCenter.LoadByID(ctx, tx, scope, req.TrustCenterID); err != nil { - return fmt.Errorf("cannot load trust center: %w", err) + compliancePage := &coredata.TrustCenter{} + if err := compliancePage.LoadByID(ctx, tx, scope, req.TrustCenterID); err != nil { + return fmt.Errorf("cannot load compliance page: %w", err) } fileID, s3Key, err := s.uploadReferenceLogoFile(ctx, scope, tx, req.LogoFile, referenceID, req.TrustCenterID, now) @@ -191,7 +191,7 @@ func (s *Service) CreateReference( reference = &coredata.TrustCenterReference{ ID: referenceID, - OrganizationID: trustCenter.OrganizationID, + OrganizationID: compliancePage.OrganizationID, TrustCenterID: req.TrustCenterID, Name: req.Name, Description: req.Description, @@ -202,7 +202,7 @@ func (s *Service) CreateReference( } if err := reference.Insert(ctx, tx, scope); err != nil { - return fmt.Errorf("cannot insert trust center reference: %w", err) + return fmt.Errorf("cannot insert compliance page reference: %w", err) } return nil @@ -239,7 +239,7 @@ func (s *Service) UpdateReference( reference = &coredata.TrustCenterReference{} if err := reference.LoadByID(ctx, tx, scope, req.ID); err != nil { - return fmt.Errorf("cannot load trust center reference: %w", err) + return fmt.Errorf("cannot load compliance page reference: %w", err) } if req.LogoFile != nil { @@ -278,7 +278,7 @@ func (s *Service) UpdateReference( } if err := reference.Update(ctx, tx, scope); err != nil { - return fmt.Errorf("cannot update trust center reference: %w", err) + return fmt.Errorf("cannot update compliance page reference: %w", err) } return nil @@ -303,11 +303,11 @@ func (s *Service) DeleteReference( reference := &coredata.TrustCenterReference{} if err := reference.LoadByID(ctx, tx, scope, trustCenterReferenceID); err != nil { - return fmt.Errorf("cannot load trust center reference: %w", err) + return fmt.Errorf("cannot load compliance page reference: %w", err) } if err := reference.Delete(ctx, tx, scope); err != nil { - return fmt.Errorf("cannot delete trust center reference: %w", err) + return fmt.Errorf("cannot delete compliance page reference: %w", err) } return nil @@ -331,7 +331,7 @@ func (s *Service) GenerateReferenceLogoURL( }, ) if err != nil { - return "", fmt.Errorf("cannot load trust center reference: %w", err) + return "", fmt.Errorf("cannot load compliance page reference: %w", err) } file, err := s.fileManager.GetPublicFile(ctx, reference.LogoFileID) @@ -348,7 +348,7 @@ func (s *Service) uploadReferenceLogoFile( tx pg.Tx, file File, referenceID gid.GID, - trustCenterID gid.GID, + compliancePageID gid.GID, now time.Time, ) (gid.GID, string, error) { fileID := gid.New(scope.GetTenantID(), coredata.FileEntityType) @@ -358,9 +358,9 @@ func (s *Service) uploadReferenceLogoFile( return gid.GID{}, "", fmt.Errorf("cannot generate object key: %w", err) } - trustCenter := &coredata.TrustCenter{} - if err := trustCenter.LoadByID(ctx, tx, scope, trustCenterID); err != nil { - return gid.GID{}, "", fmt.Errorf("cannot load trust center: %w", err) + compliancePage := &coredata.TrustCenter{} + if err := compliancePage.LoadByID(ctx, tx, scope, compliancePageID); err != nil { + return gid.GID{}, "", fmt.Errorf("cannot load compliance page: %w", err) } var ( @@ -418,9 +418,9 @@ func (s *Service) uploadReferenceLogoFile( ContentType: new(contentType), CacheControl: new("max-age=3600, public"), Metadata: map[string]string{ - "type": "trust-center-reference-logo", - "trust-center-reference-id": referenceID.String(), - "organization-id": trustCenter.OrganizationID.String(), + "type": "compliance-page-reference-logo", + "compliance-page-reference-id": referenceID.String(), + "organization-id": compliancePage.OrganizationID.String(), }, }, ) @@ -430,7 +430,7 @@ func (s *Service) uploadReferenceLogoFile( fileRecord := &coredata.File{ ID: fileID, - OrganizationID: trustCenter.OrganizationID, + OrganizationID: compliancePage.OrganizationID, BucketName: s.bucket, MimeType: contentType, FileName: filename, diff --git a/pkg/complianceportal/management/service.go b/pkg/complianceportal/management/service.go index e3f82ca2e..292c92fd2 100644 --- a/pkg/complianceportal/management/service.go +++ b/pkg/complianceportal/management/service.go @@ -13,7 +13,7 @@ // PERFORMANCE OF THIS SOFTWARE. // Package management holds the scoped, admin-facing compliance portal services -// (trust center CRUD, domains, frameworks, external URLs, references, files and +// (compliance page CRUD, domains, frameworks, external URLs, references, files and // accesses). It is the write side of the compliance portal feature. package management @@ -37,7 +37,7 @@ const ( type ( // Service is the admin-facing compliance portal service. It exposes the - // scoped CRUD operations for the trust center and its related resources as + // scoped CRUD operations for the compliance page and its related resources as // methods on a single type. Service struct { pg *pg.Client diff --git a/pkg/complianceportal/brand.go b/pkg/complianceportal/visitor/brand.go similarity index 98% rename from pkg/complianceportal/brand.go rename to pkg/complianceportal/visitor/brand.go index 56973974f..fca799724 100644 --- a/pkg/complianceportal/brand.go +++ b/pkg/complianceportal/visitor/brand.go @@ -12,7 +12,7 @@ // OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. -package complianceportal +package visitor import ( "fmt" diff --git a/pkg/complianceportal/cimd.go b/pkg/complianceportal/visitor/cimd.go similarity index 99% rename from pkg/complianceportal/cimd.go rename to pkg/complianceportal/visitor/cimd.go index 6b2bd671d..6f07fbc7f 100644 --- a/pkg/complianceportal/cimd.go +++ b/pkg/complianceportal/visitor/cimd.go @@ -12,7 +12,7 @@ // OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. -package complianceportal +package visitor import ( "fmt" diff --git a/pkg/complianceportal/cimd_test.go b/pkg/complianceportal/visitor/cimd_test.go similarity index 95% rename from pkg/complianceportal/cimd_test.go rename to pkg/complianceportal/visitor/cimd_test.go index 38b4ec750..b337369ff 100644 --- a/pkg/complianceportal/cimd_test.go +++ b/pkg/complianceportal/visitor/cimd_test.go @@ -12,7 +12,7 @@ // OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. -package complianceportal +package visitor import ( "testing" @@ -66,7 +66,7 @@ func TestBuildClientMetadataDocument(t *testing.T) { websiteURL := "https://www.acme.com" portal := &coredata.TrustCenter{ - Title: "Acme Trust Center", + Title: "Acme Compliance Page", WebsiteURL: &websiteURL, } @@ -76,7 +76,7 @@ func TestBuildClientMetadataDocument(t *testing.T) { ) require.NoError(t, err) assert.Equal(t, "https://acme.example.com/.well-known/oauth-client-metadata", doc.ClientID) - assert.Equal(t, "Acme Trust Center", doc.ClientName) + assert.Equal(t, "Acme Compliance Page", doc.ClientName) assert.Equal(t, []string{"https://acme.example.com/callback"}, doc.RedirectURIs) assert.Equal(t, "https://acme.example.com", doc.ClientURI) assert.Equal(t, VisitorOAuthScope, doc.Scope) @@ -88,7 +88,7 @@ func TestBuildClientMetadataDocument_LogoURIUsesBrandLogoEndpoint(t *testing.T) logoFileID := gid.MustParseGID("WR-qMrB5AAEAGQAAAZ9mIO8B8vDFQ-i3") portal := &coredata.TrustCenter{ - Title: "Acme Trust Center", + Title: "Acme Compliance Page", LogoFileID: &logoFileID, } diff --git a/pkg/complianceportal/visitor/compliance_framework_service.go b/pkg/complianceportal/visitor/compliance_framework_service.go index 594bdeb79..de7c6f32d 100644 --- a/pkg/complianceportal/visitor/compliance_framework_service.go +++ b/pkg/complianceportal/visitor/compliance_framework_service.go @@ -58,7 +58,7 @@ func (s *Service) GetComplianceFramework( func (s *Service) ListComplianceFrameworksByPortalID( ctx context.Context, scope coredata.Scoper, - trustCenterID gid.GID, + compliancePageID gid.GID, cursor *page.Cursor[coredata.ComplianceFrameworkOrderField], ) (*page.Page[*coredata.ComplianceFramework, coredata.ComplianceFrameworkOrderField], error) { var complianceFrameworks coredata.ComplianceFrameworks @@ -66,7 +66,7 @@ func (s *Service) ListComplianceFrameworksByPortalID( err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - err := complianceFrameworks.LoadByTrustCenterID(ctx, conn, scope, trustCenterID, cursor) + err := complianceFrameworks.LoadByTrustCenterID(ctx, conn, scope, compliancePageID, cursor) if err != nil { return fmt.Errorf("cannot load compliance frameworks: %w", err) } diff --git a/pkg/complianceportal/visitor/compliance_page_service.go b/pkg/complianceportal/visitor/compliance_page_service.go index 9ae2debb0..bf4865d87 100644 --- a/pkg/complianceportal/visitor/compliance_page_service.go +++ b/pkg/complianceportal/visitor/compliance_page_service.go @@ -122,40 +122,40 @@ type ( func (s *Service) RenderCompliancePageMarkdown( ctx context.Context, w io.Writer, - trustCenterID gid.GID, + compliancePageID gid.GID, scope coredata.Scoper, ) error { - org, err := s.GetPortalOrganization(ctx, trustCenterID) + org, err := s.GetPortalOrganization(ctx, compliancePageID) if err != nil { return fmt.Errorf("cannot load organization for compliance page: %w", err) } - trustCenter, err := s.GetPortalByID(ctx, trustCenterID) + compliancePage, err := s.GetPortalByID(ctx, compliancePageID) if err != nil { - return fmt.Errorf("cannot load trust center for compliance page: %w", err) + return fmt.Errorf("cannot load compliance page: %w", err) } data := &compliancePageData{ OrgName: org.Name, } - if trustCenter.Description != nil && *trustCenter.Description != "" { - data.Description = *trustCenter.Description + if compliancePage.Description != nil && *compliancePage.Description != "" { + data.Description = *compliancePage.Description } - if trustCenter.WebsiteURL != nil && *trustCenter.WebsiteURL != "" { - data.Details = append(data.Details, compliancePageDetail{Label: "Website", Value: *trustCenter.WebsiteURL}) + if compliancePage.WebsiteURL != nil && *compliancePage.WebsiteURL != "" { + data.Details = append(data.Details, compliancePageDetail{Label: "Website", Value: *compliancePage.WebsiteURL}) } - if trustCenter.Email != nil && *trustCenter.Email != "" { - data.Details = append(data.Details, compliancePageDetail{Label: "Email", Value: *trustCenter.Email}) + if compliancePage.Email != nil && *compliancePage.Email != "" { + data.Details = append(data.Details, compliancePageDetail{Label: "Email", Value: *compliancePage.Email}) } - if trustCenter.HeadquarterAddress != nil && *trustCenter.HeadquarterAddress != "" { - data.Details = append(data.Details, compliancePageDetail{Label: "Headquarters", Value: *trustCenter.HeadquarterAddress}) + if compliancePage.HeadquarterAddress != nil && *compliancePage.HeadquarterAddress != "" { + data.Details = append(data.Details, compliancePageDetail{Label: "Headquarters", Value: *compliancePage.HeadquarterAddress}) } - data.Frameworks, err = s.fetchComplianceFrameworks(ctx, scope, trustCenterID) + data.Frameworks, err = s.fetchComplianceFrameworks(ctx, scope, compliancePageID) if err != nil { return fmt.Errorf("cannot fetch compliance frameworks: %w", err) } @@ -175,12 +175,12 @@ func (s *Service) RenderCompliancePageMarkdown( return fmt.Errorf("cannot fetch thirdParties: %w", err) } - data.References, err = s.fetchReferences(ctx, scope, trustCenterID) + data.References, err = s.fetchReferences(ctx, scope, compliancePageID) if err != nil { return fmt.Errorf("cannot fetch references: %w", err) } - data.CustomLinks, err = s.fetchCustomLinks(ctx, scope, trustCenterID) + data.CustomLinks, err = s.fetchCustomLinks(ctx, scope, compliancePageID) if err != nil { return fmt.Errorf("cannot fetch external links: %w", err) } @@ -207,11 +207,11 @@ type ( func (s *Service) RenderSitemap( ctx context.Context, w io.Writer, - trustCenterID gid.GID, + compliancePageID gid.GID, scope coredata.Scoper, baseURL string, ) error { - org, err := s.GetPortalOrganization(ctx, trustCenterID) + org, err := s.GetPortalOrganization(ctx, compliancePageID) if err != nil { return fmt.Errorf("cannot load organization for sitemap: %w", err) } @@ -322,7 +322,7 @@ func (s *Service) fetchDocumentIDs( coredata.NewTrustCenterFileFilter(), ) if err != nil { - return nil, fmt.Errorf("cannot list trust center files: %w", err) + return nil, fmt.Errorf("cannot list compliance page files: %w", err) } for _, file := range result.Data { @@ -401,7 +401,7 @@ func (s *Service) fetchDocumentIDs( func (s *Service) fetchComplianceFrameworks( ctx context.Context, scope coredata.Scoper, - trustCenterID gid.GID, + compliancePageID gid.GID, ) ([]compliancePageFramework, error) { var frameworks []compliancePageFramework @@ -417,7 +417,7 @@ func (s *Service) fetchComplianceFrameworks( }, ) - result, err := s.ListComplianceFrameworksByPortalID(ctx, scope, trustCenterID, cursor) + result, err := s.ListComplianceFrameworksByPortalID(ctx, scope, compliancePageID, cursor) if err != nil { return nil, fmt.Errorf("cannot list compliance frameworks: %w", err) } @@ -621,7 +621,7 @@ func (s *Service) fetchThirdParties( func (s *Service) fetchReferences( ctx context.Context, scope coredata.Scoper, - trustCenterID gid.GID, + compliancePageID gid.GID, ) ([]compliancePageReference, error) { var refs []compliancePageReference @@ -637,7 +637,7 @@ func (s *Service) fetchReferences( }, ) - result, err := s.ListPortalReferencesForPortalID(ctx, scope, trustCenterID, cursor) + result, err := s.ListPortalReferencesForPortalID(ctx, scope, compliancePageID, cursor) if err != nil { return nil, fmt.Errorf("cannot list references: %w", err) } @@ -669,7 +669,7 @@ func (s *Service) fetchReferences( func (s *Service) fetchCustomLinks( ctx context.Context, scope coredata.Scoper, - trustCenterID gid.GID, + compliancePageID gid.GID, ) ([]compliancePageCustomLink, error) { var links []compliancePageCustomLink @@ -685,7 +685,7 @@ func (s *Service) fetchCustomLinks( }, ) - result, err := s.ListCustomLinksForPortalID(ctx, scope, trustCenterID, cursor) + result, err := s.ListCustomLinksForPortalID(ctx, scope, compliancePageID, cursor) if err != nil { return nil, fmt.Errorf("cannot list custom links: %w", err) } diff --git a/pkg/complianceportal/visitor/custom_link_service.go b/pkg/complianceportal/visitor/custom_link_service.go index de8192017..2580683fb 100644 --- a/pkg/complianceportal/visitor/custom_link_service.go +++ b/pkg/complianceportal/visitor/custom_link_service.go @@ -33,7 +33,7 @@ import ( func (s *Service) ListCustomLinksForPortalID( ctx context.Context, scope coredata.Scoper, - trustCenterID gid.GID, + compliancePageID gid.GID, cursor *page.Cursor[coredata.ComplianceCustomLinkOrderField], ) (*page.Page[*coredata.ComplianceCustomLink, coredata.ComplianceCustomLinkOrderField], error) { var links coredata.ComplianceCustomLinks @@ -41,7 +41,7 @@ func (s *Service) ListCustomLinksForPortalID( err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - err := links.LoadByTrustCenterID(ctx, conn, scope, trustCenterID, cursor) + err := links.LoadByTrustCenterID(ctx, conn, scope, compliancePageID, cursor) if err != nil { return fmt.Errorf("cannot load custom links: %w", err) } diff --git a/pkg/complianceportal/visitor/document_service.go b/pkg/complianceportal/visitor/document_service.go index 6c3dae866..b728f80ca 100644 --- a/pkg/complianceportal/visitor/document_service.go +++ b/pkg/complianceportal/visitor/document_service.go @@ -159,7 +159,7 @@ func (s *Service) exportDocumentPDFData( } if document.TrustCenterVisibility == coredata.TrustCenterVisibilityNone { - return fmt.Errorf("document not visible on trust center") + return fmt.Errorf("document not visible on compliance page") } if err := version.LoadLatestPublishedVersion(ctx, conn, scope, documentID); err != nil { diff --git a/pkg/complianceportal/visitor/errors.go b/pkg/complianceportal/visitor/errors.go index e07cbbfdf..ca539acfb 100644 --- a/pkg/complianceportal/visitor/errors.go +++ b/pkg/complianceportal/visitor/errors.go @@ -23,17 +23,17 @@ package visitor import "errors" var ( - ErrOAuthStateNotFound = errors.New("oauth state not found") - ErrOAuthStateExpired = errors.New("oauth state expired") - ErrPageNotFound = errors.New("page not found") - ErrMembershipNotFound = errors.New("membership not found") - ErrUserNotFound = errors.New("user not found") - ErrUserInactive = errors.New("user inactive") - ErrDocumentAccessNotFound = errors.New("document access not found") - ErrNDAFileNotFound = errors.New("NDA file not found") - ErrDocumentNotFound = errors.New("document not found") - ErrDocumentNotVisible = errors.New("document not visible") - ErrReportNotFound = errors.New("report not found") - ErrTrustCenterFileNotFound = errors.New("trust center file not found") - ErrTrustCenterFileNotVisible = errors.New("trust center file not visible") + ErrOAuthStateNotFound = errors.New("oauth state not found") + ErrOAuthStateExpired = errors.New("oauth state expired") + ErrPageNotFound = errors.New("page not found") + ErrMembershipNotFound = errors.New("membership not found") + ErrUserNotFound = errors.New("user not found") + ErrUserInactive = errors.New("user inactive") + ErrDocumentAccessNotFound = errors.New("document access not found") + ErrNDAFileNotFound = errors.New("NDA file not found") + ErrDocumentNotFound = errors.New("document not found") + ErrDocumentNotVisible = errors.New("document not visible") + ErrReportNotFound = errors.New("report not found") + ErrPortalFileNotFound = errors.New("portal file not found") + ErrPortalFileNotVisible = errors.New("portal file not visible") ) diff --git a/pkg/complianceportal/visitor/portal_access_service.go b/pkg/complianceportal/visitor/portal_access_service.go index 1786be118..d3067b38d 100644 --- a/pkg/complianceportal/visitor/portal_access_service.go +++ b/pkg/complianceportal/visitor/portal_access_service.go @@ -60,9 +60,9 @@ func (s *Service) RequestPortalAccess( err := s.pg.WithTx( ctx, func(ctx context.Context, tx pg.Tx) error { - trustCenter := &coredata.TrustCenter{} - if err := trustCenter.LoadByID(ctx, tx, scope, req.TrustCenterID); err != nil { - return fmt.Errorf("cannot load trust center: %w", err) + compliancePage := &coredata.TrustCenter{} + if err := compliancePage.LoadByID(ctx, tx, scope, req.TrustCenterID); err != nil { + return fmt.Errorf("cannot load compliance page: %w", err) } access = &coredata.TrustCenterAccess{} @@ -70,7 +70,7 @@ func (s *Service) RequestPortalAccess( return fmt.Errorf("cannot load compliance page membership: %w", err) } - organizationID := trustCenter.OrganizationID + organizationID := compliancePage.OrganizationID documentIDs := req.DocumentIDs if req.DocumentIDs == nil { @@ -145,7 +145,7 @@ func (s *Service) RequestPortalAccess( func(ctx context.Context, cursor *page.Cursor[coredata.TrustCenterFileOrderField]) ([]*coredata.TrustCenterFile, error) { var batch coredata.TrustCenterFiles if err := batch.LoadByOrganizationID(ctx, tx, scope, organizationID, cursor, filter); err != nil { - return nil, fmt.Errorf("cannot list trust center files: %w", err) + return nil, fmt.Errorf("cannot list compliance page files: %w", err) } return batch, nil @@ -196,7 +196,7 @@ func (s *Service) RequestPortalAccess( coredata.TrustCenterDocumentAccessStatusRequested, now, ); err != nil { - return fmt.Errorf("cannot bulk insert trust center document accesses: %w", err) + return fmt.Errorf("cannot bulk insert compliance page document accesses: %w", err) } if err := accesses.BulkInsertReportFileAccesses( @@ -209,7 +209,7 @@ func (s *Service) RequestPortalAccess( coredata.TrustCenterDocumentAccessStatusRequested, now, ); err != nil { - return fmt.Errorf("cannot bulk insert trust center report accesses: %w", err) + return fmt.Errorf("cannot bulk insert compliance page report accesses: %w", err) } if err := accesses.BulkInsertTrustCenterFileAccesses( @@ -222,7 +222,7 @@ func (s *Service) RequestPortalAccess( coredata.TrustCenterDocumentAccessStatusRequested, now, ); err != nil { - return fmt.Errorf("cannot bulk insert trust center file accesses: %w", err) + return fmt.Errorf("cannot bulk insert compliance page file accesses: %w", err) } return nil @@ -242,7 +242,7 @@ func (s *Service) RequestPortalAccess( func (s *Service) GetPortalAccess( ctx context.Context, scope coredata.Scoper, - trustCenterID gid.GID, + compliancePageID gid.GID, identityID gid.GID, ) (coredata.TrustCenterAccess, error) { var access coredata.TrustCenterAccess @@ -250,7 +250,7 @@ func (s *Service) GetPortalAccess( err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - return access.LoadByTrustCenterIDAndIdentityID(ctx, conn, scope, trustCenterID, identityID) + return access.LoadByTrustCenterIDAndIdentityID(ctx, conn, scope, compliancePageID, identityID) }, ) @@ -260,7 +260,7 @@ func (s *Service) GetPortalAccess( func (s *Service) GetPortalDocumentAccess( ctx context.Context, scope coredata.Scoper, - trustCenterID gid.GID, + compliancePageID gid.GID, identityID gid.GID, documentID gid.GID, ) (*coredata.TrustCenterDocumentAccess, error) { @@ -271,13 +271,13 @@ func (s *Service) GetPortalDocumentAccess( func(ctx context.Context, conn pg.Querier) error { access := &coredata.TrustCenterAccess{} - err := access.LoadByTrustCenterIDAndIdentityID(ctx, conn, scope, trustCenterID, identityID) + err := access.LoadByTrustCenterIDAndIdentityID(ctx, conn, scope, compliancePageID, identityID) if err != nil { if errors.Is(err, coredata.ErrResourceNotFound) { return ErrMembershipNotFound } - return fmt.Errorf("cannot load trust center access: %w", err) + return fmt.Errorf("cannot load compliance page access: %w", err) } profile := &coredata.MembershipProfile{} @@ -315,7 +315,7 @@ func (s *Service) GetPortalDocumentAccess( func (s *Service) GetPortalReportFileAccess( ctx context.Context, scope coredata.Scoper, - trustCenterID gid.GID, + compliancePageID gid.GID, identityID gid.GID, reportFileID gid.GID, ) (*coredata.TrustCenterDocumentAccess, error) { @@ -326,13 +326,13 @@ func (s *Service) GetPortalReportFileAccess( func(ctx context.Context, conn pg.Querier) error { access := &coredata.TrustCenterAccess{} - err := access.LoadByTrustCenterIDAndIdentityID(ctx, conn, scope, trustCenterID, identityID) + err := access.LoadByTrustCenterIDAndIdentityID(ctx, conn, scope, compliancePageID, identityID) if err != nil { if errors.Is(err, coredata.ErrResourceNotFound) { return ErrMembershipNotFound } - return fmt.Errorf("cannot load trust center access: %w", err) + return fmt.Errorf("cannot load compliance page access: %w", err) } profile := &coredata.MembershipProfile{} @@ -370,7 +370,7 @@ func (s *Service) GetPortalReportFileAccess( func (s *Service) GetPortalFileAccess( ctx context.Context, scope coredata.Scoper, - trustCenterID gid.GID, + compliancePageID gid.GID, identityID gid.GID, trustCenterFileID gid.GID, ) (*coredata.TrustCenterDocumentAccess, error) { @@ -381,13 +381,13 @@ func (s *Service) GetPortalFileAccess( func(ctx context.Context, conn pg.Querier) error { access := &coredata.TrustCenterAccess{} - err := access.LoadByTrustCenterIDAndIdentityID(ctx, conn, scope, trustCenterID, identityID) + err := access.LoadByTrustCenterIDAndIdentityID(ctx, conn, scope, compliancePageID, identityID) if err != nil { if errors.Is(err, coredata.ErrResourceNotFound) { return ErrMembershipNotFound } - return fmt.Errorf("cannot load trust center access: %w", err) + return fmt.Errorf("cannot load compliance page access: %w", err) } profile := &coredata.MembershipProfile{} @@ -409,7 +409,7 @@ func (s *Service) GetPortalFileAccess( return ErrDocumentAccessNotFound } - return fmt.Errorf("cannot load trust center file access: %w", err) + return fmt.Errorf("cannot load compliance page file access: %w", err) } return nil @@ -434,9 +434,9 @@ func (s *Service) GrantPortalAccessByIDs( return s.pg.WithTx( ctx, func(ctx context.Context, tx pg.Tx) error { - trustCenter := &coredata.TrustCenter{} - if err := trustCenter.LoadByOrganizationID(ctx, tx, scope, organizationID); err != nil { - return fmt.Errorf("cannot load trust center: %w", err) + compliancePage := &coredata.TrustCenter{} + if err := compliancePage.LoadByOrganizationID(ctx, tx, scope, organizationID); err != nil { + return fmt.Errorf("cannot load compliance page: %w", err) } identity := &coredata.Identity{} @@ -445,8 +445,8 @@ func (s *Service) GrantPortalAccessByIDs( } access := &coredata.TrustCenterAccess{} - if err := access.LoadByTrustCenterIDAndIdentityID(ctx, tx, scope, trustCenter.ID, identity.ID); err != nil { - return fmt.Errorf("cannot load trust center access: %w", err) + if err := access.LoadByTrustCenterIDAndIdentityID(ctx, tx, scope, compliancePage.ID, identity.ID); err != nil { + return fmt.Errorf("cannot load compliance page access: %w", err) } profile := &coredata.MembershipProfile{} @@ -477,7 +477,7 @@ func (s *Service) GrantPortalAccessByIDs( if len(fileIDs) > 0 { if err := coredata.GrantByTrustCenterFileIDs(ctx, tx, scope, access.ID, fileIDs, now); err != nil { - return fmt.Errorf("cannot grant trust center file accesses: %w", err) + return fmt.Errorf("cannot grant compliance page file accesses: %w", err) } } @@ -515,7 +515,7 @@ func (s *Service) sendPortalAccessEmail( access.UpdatedAt = now if err := access.Update(ctx, tx, scope); err != nil { - return fmt.Errorf("cannot update trust center access with expiration: %w", err) + return fmt.Errorf("cannot update compliance page access with expiration: %w", err) } emailPresenterCfg, err := s.GetPortalEmailPresenterConfig(ctx, scope, access.TrustCenterID) @@ -527,7 +527,7 @@ func (s *Service) sendPortalAccessEmail( subject, textBody, htmlBody, err := emailPresenter.RenderTrustCenterAccess(ctx, organization.Name) if err != nil { - return fmt.Errorf("cannot render trust center access email: %w", err) + return fmt.Errorf("cannot render compliance page access email: %w", err) } accessEmail := coredata.NewEmail( @@ -560,9 +560,9 @@ func (s *Service) RejectOrRevokePortalAccessByIDs( return s.pg.WithTx( ctx, func(ctx context.Context, tx pg.Tx) error { - trustCenter := &coredata.TrustCenter{} - if err := trustCenter.LoadByOrganizationID(ctx, tx, scope, organizationID); err != nil { - return fmt.Errorf("cannot load trust center: %w", err) + compliancePage := &coredata.TrustCenter{} + if err := compliancePage.LoadByOrganizationID(ctx, tx, scope, organizationID); err != nil { + return fmt.Errorf("cannot load compliance page: %w", err) } identity := &coredata.Identity{} @@ -571,8 +571,8 @@ func (s *Service) RejectOrRevokePortalAccessByIDs( } access := &coredata.TrustCenterAccess{} - if err := access.LoadByTrustCenterIDAndIdentityID(ctx, tx, scope, trustCenter.ID, identity.ID); err != nil { - return fmt.Errorf("cannot load trust center access: %w", err) + if err := access.LoadByTrustCenterIDAndIdentityID(ctx, tx, scope, compliancePage.ID, identity.ID); err != nil { + return fmt.Errorf("cannot load compliance page access: %w", err) } profile := &coredata.MembershipProfile{} @@ -603,7 +603,7 @@ func (s *Service) RejectOrRevokePortalAccessByIDs( shouldSendEmail = true if err := coredata.RejectOrRevokeByTrustCenterFileIDs(ctx, tx, scope, access.ID, fileIDs, now); err != nil { - return fmt.Errorf("cannot reject/revoke trust center file accesses: %w", err) + return fmt.Errorf("cannot reject/revoke compliance page file accesses: %w", err) } } @@ -681,7 +681,7 @@ func (s *Service) sendPortalDocumentAccessRejectedEmail( organization.Name, ) if err != nil { - return fmt.Errorf("cannot render trust center documents access rejected email: %w", err) + return fmt.Errorf("cannot render compliance page documents access rejected email: %w", err) } accessEmail := coredata.NewEmail( diff --git a/pkg/complianceportal/visitor/portal_file_service.go b/pkg/complianceportal/visitor/portal_file_service.go index fb3d2553f..2b70118e6 100644 --- a/pkg/complianceportal/visitor/portal_file_service.go +++ b/pkg/complianceportal/visitor/portal_file_service.go @@ -47,7 +47,7 @@ func (s *Service) GetPortalFile( func(ctx context.Context, conn pg.Querier) error { err := trustCenterFile.LoadByID(ctx, conn, scope, trustCenterFileID) if err != nil { - return fmt.Errorf("cannot load trust center file: %w", err) + return fmt.Errorf("cannot load compliance page file: %w", err) } return nil @@ -58,11 +58,11 @@ func (s *Service) GetPortalFile( } if trustCenterFile.OrganizationID != organizationID { - return nil, ErrTrustCenterFileNotFound + return nil, ErrPortalFileNotFound } if trustCenterFile.TrustCenterVisibility == coredata.TrustCenterVisibilityNone { - return nil, ErrTrustCenterFileNotVisible + return nil, ErrPortalFileNotVisible } return trustCenterFile, nil @@ -82,7 +82,7 @@ func (s *Service) ListPortalFilesForOrganizationID( func(ctx context.Context, conn pg.Querier) error { err := trustCenterFiles.LoadByOrganizationID(ctx, conn, scope, organizationID, cursor, filter) if err != nil { - return fmt.Errorf("cannot load trust center files: %w", err) + return fmt.Errorf("cannot load compliance page files: %w", err) } return nil @@ -103,7 +103,7 @@ func (s *Service) ExportPortalFile( ) ([]byte, string, error) { fileData, mimeType, err := s.exportPortalFileData(ctx, scope, trustCenterFileID) if err != nil { - return nil, "", fmt.Errorf("cannot export trust center file: %w", err) + return nil, "", fmt.Errorf("cannot export compliance page file: %w", err) } if mimeType == "application/pdf" { @@ -141,7 +141,7 @@ func (s *Service) exportPortalFileData( func(ctx context.Context, conn pg.Querier) error { trustCenterFile = &coredata.TrustCenterFile{} if err := trustCenterFile.LoadByID(ctx, conn, scope, trustCenterFileID); err != nil { - return fmt.Errorf("cannot load trust center file: %w", err) + return fmt.Errorf("cannot load compliance page file: %w", err) } file = &coredata.File{} diff --git a/pkg/complianceportal/visitor/portal_reference_service.go b/pkg/complianceportal/visitor/portal_reference_service.go index 06de68ba6..88498ddd2 100644 --- a/pkg/complianceportal/visitor/portal_reference_service.go +++ b/pkg/complianceportal/visitor/portal_reference_service.go @@ -33,7 +33,7 @@ import ( func (s *Service) ListPortalReferencesForPortalID( ctx context.Context, scope coredata.Scoper, - trustCenterID gid.GID, + compliancePageID gid.GID, cursor *page.Cursor[coredata.TrustCenterReferenceOrderField], ) (*page.Page[*coredata.TrustCenterReference, coredata.TrustCenterReferenceOrderField], error) { var references coredata.TrustCenterReferences @@ -43,9 +43,9 @@ func (s *Service) ListPortalReferencesForPortalID( ctx, func(ctx context.Context, conn pg.Querier) error { - err := references.LoadByTrustCenterID(ctx, conn, scope, trustCenterID, cursor) + err := references.LoadByTrustCenterID(ctx, conn, scope, compliancePageID, cursor) if err != nil { - return fmt.Errorf("cannot load trust center references: %w", err) + return fmt.Errorf("cannot load compliance page references: %w", err) } return nil @@ -72,7 +72,7 @@ func (s *Service) GeneratePortalReferenceLogoURL( }, ) if err != nil { - return "", fmt.Errorf("cannot load trust center reference: %w", err) + return "", fmt.Errorf("cannot load compliance page reference: %w", err) } file, err := s.fileManager.GetPublicFile(ctx, reference.LogoFileID) @@ -95,7 +95,7 @@ func (s *Service) GetPortalReference( func(ctx context.Context, conn pg.Querier) error { err := reference.LoadByID(ctx, conn, scope, referenceID) if err != nil { - return fmt.Errorf("cannot load trust center reference: %w", err) + return fmt.Errorf("cannot load compliance page reference: %w", err) } return nil diff --git a/pkg/complianceportal/visitor/portal_service.go b/pkg/complianceportal/visitor/portal_service.go index 68bbadd07..23d02c2a5 100644 --- a/pkg/complianceportal/visitor/portal_service.go +++ b/pkg/complianceportal/visitor/portal_service.go @@ -28,7 +28,6 @@ import ( "go.gearno.de/kit/pg" "go.probo.inc/probo/packages/emails" - "go.probo.inc/probo/pkg/complianceportal" "go.probo.inc/probo/pkg/coredata" "go.probo.inc/probo/pkg/gid" ) @@ -36,26 +35,26 @@ import ( func (s *Service) GetPortal( ctx context.Context, scope coredata.Scoper, - trustCenterID gid.GID, + compliancePageID gid.GID, ) (*coredata.TrustCenter, error) { - var trustCenter *coredata.TrustCenter + var compliancePage *coredata.TrustCenter err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - trustCenter = &coredata.TrustCenter{} - if err := trustCenter.LoadByID(ctx, conn, scope, trustCenterID); err != nil { - return fmt.Errorf("cannot load trust center: %w", err) + compliancePage = &coredata.TrustCenter{} + if err := compliancePage.LoadByID(ctx, conn, scope, compliancePageID); err != nil { + return fmt.Errorf("cannot load compliance page: %w", err) } return nil }, ) if err != nil { - return nil, fmt.Errorf("cannot load trust center: %w", err) + return nil, fmt.Errorf("cannot load compliance page: %w", err) } - return trustCenter, nil + return compliancePage, nil } func (s *Service) GetPortalByOrganizationID( @@ -63,14 +62,14 @@ func (s *Service) GetPortalByOrganizationID( scope coredata.Scoper, organizationID gid.GID, ) (*coredata.TrustCenter, error) { - trustCenter := &coredata.TrustCenter{} + compliancePage := &coredata.TrustCenter{} err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - err := trustCenter.LoadByOrganizationID(ctx, conn, scope, organizationID) + err := compliancePage.LoadByOrganizationID(ctx, conn, scope, organizationID) if err != nil { - return fmt.Errorf("cannot load trust center: %w", err) + return fmt.Errorf("cannot load compliance page: %w", err) } return nil @@ -80,30 +79,30 @@ func (s *Service) GetPortalByOrganizationID( return nil, err } - return trustCenter, nil + return compliancePage, nil } func (s *Service) GetPortalNDAFile( ctx context.Context, scope coredata.Scoper, - trustCenterID gid.GID, + compliancePageID gid.GID, ) (*coredata.File, error) { var file *coredata.File err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - trustCenter := &coredata.TrustCenter{} - if err := trustCenter.LoadByID(ctx, conn, scope, trustCenterID); err != nil { - return fmt.Errorf("cannot load trust center: %w", err) + compliancePage := &coredata.TrustCenter{} + if err := compliancePage.LoadByID(ctx, conn, scope, compliancePageID); err != nil { + return fmt.Errorf("cannot load compliance page: %w", err) } - if trustCenter.NonDisclosureAgreementFileID == nil { + if compliancePage.NonDisclosureAgreementFileID == nil { return nil } file = &coredata.File{} - if err := file.LoadByID(ctx, conn, scope, *trustCenter.NonDisclosureAgreementFileID); err != nil { + if err := file.LoadByID(ctx, conn, scope, *compliancePage.NonDisclosureAgreementFileID); err != nil { return fmt.Errorf("cannot load file: %w", err) } @@ -120,7 +119,7 @@ func (s *Service) GetPortalNDAFile( func (s *Service) GeneratePortalNDAFileURL( ctx context.Context, scope coredata.Scoper, - trustCenterID gid.GID, + compliancePageID gid.GID, expiresIn time.Duration, ) (string, error) { var file *coredata.File @@ -128,17 +127,17 @@ func (s *Service) GeneratePortalNDAFileURL( err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - trustCenter := &coredata.TrustCenter{} - if err := trustCenter.LoadByID(ctx, conn, scope, trustCenterID); err != nil { - return fmt.Errorf("cannot load trust center: %w", err) + compliancePage := &coredata.TrustCenter{} + if err := compliancePage.LoadByID(ctx, conn, scope, compliancePageID); err != nil { + return fmt.Errorf("cannot load compliance page: %w", err) } - if trustCenter.NonDisclosureAgreementFileID == nil { + if compliancePage.NonDisclosureAgreementFileID == nil { return fmt.Errorf("no NDA file found") } file = &coredata.File{} - if err := file.LoadByID(ctx, conn, scope, *trustCenter.NonDisclosureAgreementFileID); err != nil { + if err := file.LoadByID(ctx, conn, scope, *compliancePage.NonDisclosureAgreementFileID); err != nil { return fmt.Errorf("cannot load file: %w", err) } @@ -281,12 +280,11 @@ func (s *Service) GetPortalEmailPresenterConfig( return fmt.Errorf("cannot load organization: %w", err) } - publicURL, err := complianceportal.PublicURLForTrustCenter( + publicURL, err := s.management.PublicURLForCompliancePage( ctx, conn, scope, compliancePage, - s.baseDomain, ) if err != nil { return fmt.Errorf("cannot resolve compliance page URL: %w", err) @@ -327,24 +325,24 @@ func (s *Service) GetPortalEmailPresenterConfig( func (s *Service) GetPortalMailingList( ctx context.Context, scope coredata.Scoper, - trustCenterID gid.GID, + compliancePageID gid.GID, ) (*coredata.MailingList, error) { var mailingList *coredata.MailingList err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - trustCenter := &coredata.TrustCenter{} - if err := trustCenter.LoadByID(ctx, conn, scope, trustCenterID); err != nil { - return fmt.Errorf("cannot load trust center: %w", err) + compliancePage := &coredata.TrustCenter{} + if err := compliancePage.LoadByID(ctx, conn, scope, compliancePageID); err != nil { + return fmt.Errorf("cannot load compliance page: %w", err) } - if trustCenter.MailingListID == nil { + if compliancePage.MailingListID == nil { return nil } mailingList = &coredata.MailingList{} - if err := mailingList.LoadByID(ctx, conn, scope, *trustCenter.MailingListID); err != nil { + if err := mailingList.LoadByID(ctx, conn, scope, *compliancePage.MailingListID); err != nil { return fmt.Errorf("cannot load mailing list: %w", err) } diff --git a/pkg/complianceportal/visitor/service.go b/pkg/complianceportal/visitor/service.go index 5df688b05..d3429ab94 100644 --- a/pkg/complianceportal/visitor/service.go +++ b/pkg/complianceportal/visitor/service.go @@ -30,7 +30,7 @@ import ( "go.gearno.de/kit/log" "go.gearno.de/kit/pg" "go.probo.inc/probo/packages/emails" - "go.probo.inc/probo/pkg/complianceportal" + "go.probo.inc/probo/pkg/complianceportal/management" "go.probo.inc/probo/pkg/coredata" "go.probo.inc/probo/pkg/esign" "go.probo.inc/probo/pkg/filemanager" @@ -45,7 +45,7 @@ const NDAConsentText = "By clicking \"Review and sign\", I consent to sign this type ( // Service is the visitor-facing compliance portal service. It exposes the - // public read operations for the trust center and its related resources as + // public read operations for the compliance page and its related resources as // methods on a single type. Service struct { pg *pg.Client @@ -61,6 +61,7 @@ type ( logger *log.Logger slack *slack.Service resourceAlias *resourcealias.Service + management *management.Service } ) @@ -78,6 +79,7 @@ func NewService( logger *log.Logger, slack *slack.Service, resourceAliasSvc *resourcealias.Service, + managementSvc *management.Service, ) *Service { svc := &Service{ pg: pgClient, @@ -93,6 +95,7 @@ func NewService( logger: logger, slack: slack, resourceAlias: resourceAliasSvc, + management: managementSvc, } return svc @@ -102,18 +105,18 @@ func (s *Service) GetPortalByID( ctx context.Context, id gid.GID, ) (*coredata.TrustCenter, error) { - trustCenter := &coredata.TrustCenter{} + compliancePage := &coredata.TrustCenter{} err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - err := trustCenter.LoadByID(ctx, conn, coredata.NewNoScope(), id) + err := compliancePage.LoadByID(ctx, conn, coredata.NewNoScope(), id) if err != nil { if errors.Is(err, coredata.ErrResourceNotFound) { return ErrPageNotFound } - return fmt.Errorf("cannot load trust center: %w", err) + return fmt.Errorf("cannot load compliance page: %w", err) } return nil @@ -123,25 +126,25 @@ func (s *Service) GetPortalByID( return nil, err } - return trustCenter, nil + return compliancePage, nil } func (s *Service) GetPortalBySlug( ctx context.Context, slug string, ) (*coredata.TrustCenter, error) { - trustCenter := &coredata.TrustCenter{} + compliancePage := &coredata.TrustCenter{} err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - err := trustCenter.LoadBySlug(ctx, conn, slug) + err := compliancePage.LoadBySlug(ctx, conn, slug) if err != nil { if errors.Is(err, coredata.ErrResourceNotFound) { return ErrPageNotFound } - return fmt.Errorf("cannot load trust center: %w", err) + return fmt.Errorf("cannot load compliance page: %w", err) } return nil @@ -151,25 +154,25 @@ func (s *Service) GetPortalBySlug( return nil, err } - return trustCenter, nil + return compliancePage, nil } // GetEffectiveCanonicalHost returns the host a compliance page should be // served under. It prefers the primary domain when its certificate is active, // and otherwise falls back to the managed probopage subdomain. An empty string // is returned when no serving host can be determined. -func (s *Service) GetPortalEffectiveCanonicalHost(ctx context.Context, trustCenterID gid.GID) (string, error) { +func (s *Service) GetPortalEffectiveCanonicalHost(ctx context.Context, compliancePageID gid.GID) (string, error) { var host string err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - trustCenter := &coredata.TrustCenter{} - if err := trustCenter.LoadByID(ctx, conn, coredata.NewNoScope(), trustCenterID); err != nil { - return fmt.Errorf("cannot load trust center: %w", err) + compliancePage := &coredata.TrustCenter{} + if err := compliancePage.LoadByID(ctx, conn, coredata.NewNoScope(), compliancePageID); err != nil { + return fmt.Errorf("cannot load compliance page: %w", err) } - domain, err := complianceportal.EffectiveDomainForTrustCenter(ctx, conn, coredata.NewNoScope(), trustCenter) + domain, err := s.management.EffectiveDomainForCompliancePage(ctx, conn, coredata.NewNoScope(), compliancePage) if err != nil { return err } @@ -189,7 +192,7 @@ func (s *Service) GetPortalEffectiveCanonicalHost(ctx context.Context, trustCent } func (s *Service) GetPortalByDomainName(ctx context.Context, domain string) (*coredata.TrustCenter, error) { - trustCenter := &coredata.TrustCenter{} + compliancePage := &coredata.TrustCenter{} err := s.pg.WithConn( ctx, @@ -203,13 +206,13 @@ func (s *Service) GetPortalByDomainName(ctx context.Context, domain string) (*co return fmt.Errorf("cannot load custom domain: %w", err) } - trustCenter = &coredata.TrustCenter{} - if err := trustCenter.LoadByDomainID(ctx, conn, customDomain.ID); err != nil { + compliancePage = &coredata.TrustCenter{} + if err := compliancePage.LoadByDomainID(ctx, conn, customDomain.ID); err != nil { if errors.Is(err, coredata.ErrResourceNotFound) { return ErrPageNotFound } - return fmt.Errorf("cannot load trust center: %w", err) + return fmt.Errorf("cannot load compliance page: %w", err) } return nil @@ -219,37 +222,37 @@ func (s *Service) GetPortalByDomainName(ctx context.Context, domain string) (*co return nil, err } - return trustCenter, err + return compliancePage, err } // GetPortalEmailPresenterConfigByOrganizationID resolves the emails.PresenterConfig for -// the trust center that belongs to the given organization. This is used by the +// the compliance page that belongs to the given organization. This is used by the // esign certificate worker which needs per-org branding at render time. func (s *Service) GetPortalEmailPresenterConfigByOrganizationID(ctx context.Context, orgID gid.GID) (emails.PresenterConfig, error) { - var trustCenter coredata.TrustCenter + var compliancePage coredata.TrustCenter scope := coredata.NewScopeFromObjectID(orgID) err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - return trustCenter.LoadByOrganizationID(ctx, conn, scope, orgID) + return compliancePage.LoadByOrganizationID(ctx, conn, scope, orgID) }, ) if err != nil { - return emails.PresenterConfig{}, fmt.Errorf("cannot load trust center for org %s: %w", orgID, err) + return emails.PresenterConfig{}, fmt.Errorf("cannot load compliance page for org %s: %w", orgID, err) } - return s.GetPortalEmailPresenterConfig(ctx, scope, trustCenter.ID) + return s.GetPortalEmailPresenterConfig(ctx, scope, compliancePage.ID) } func (s *Service) GetPortalOrganization( ctx context.Context, - trustCenterID gid.GID, + compliancePageID gid.GID, ) (*coredata.Organization, error) { - trustCenter, err := s.GetPortalByID(ctx, trustCenterID) + compliancePage, err := s.GetPortalByID(ctx, compliancePageID) if err != nil { - return nil, fmt.Errorf("cannot load trust center: %w", err) + return nil, fmt.Errorf("cannot load compliance page: %w", err) } org := &coredata.Organization{} @@ -257,7 +260,7 @@ func (s *Service) GetPortalOrganization( err = s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - return org.LoadByID(ctx, conn, coredata.NewNoScope(), trustCenter.OrganizationID) + return org.LoadByID(ctx, conn, coredata.NewNoScope(), compliancePage.OrganizationID) }, ) if err != nil { @@ -305,17 +308,17 @@ func (s *Service) GetPortalNDAFileByID( err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - trustCenter := &coredata.TrustCenter{} - if err := trustCenter.LoadByID(ctx, conn, scope, compliancePageID); err != nil { - return fmt.Errorf("cannot load trust center: %w", err) + compliancePage := &coredata.TrustCenter{} + if err := compliancePage.LoadByID(ctx, conn, scope, compliancePageID); err != nil { + return fmt.Errorf("cannot load compliance page: %w", err) } - if trustCenter.NonDisclosureAgreementFileID == nil { + if compliancePage.NonDisclosureAgreementFileID == nil { return ErrNDAFileNotFound } file = &coredata.File{} - if err := file.LoadByID(ctx, conn, scope, *trustCenter.NonDisclosureAgreementFileID); err != nil { + if err := file.LoadByID(ctx, conn, scope, *compliancePage.NonDisclosureAgreementFileID); err != nil { if errors.Is(err, coredata.ErrResourceNotFound) { return ErrNDAFileNotFound } @@ -349,7 +352,7 @@ func (s *Service) ProvisionPortalMember( func(ctx context.Context, tx pg.Tx) error { compliancePage := &coredata.TrustCenter{} if err := compliancePage.LoadByID(ctx, tx, scope, compliancePageID); err != nil { - return fmt.Errorf("cannot load trust center: %w", err) + return fmt.Errorf("cannot load compliance page: %w", err) } identity := &coredata.Identity{} @@ -360,7 +363,7 @@ func (s *Service) ProvisionPortalMember( access = &coredata.TrustCenterAccess{} if err := access.LoadByTrustCenterIDAndIdentityID(ctx, tx, scope, compliancePageID, identityID); err != nil { if !errors.Is(err, coredata.ErrResourceNotFound) { - return fmt.Errorf("cannot load trust center access: %w", err) + return fmt.Errorf("cannot load compliance page access: %w", err) } access = &coredata.TrustCenterAccess{ @@ -399,7 +402,7 @@ func (s *Service) ProvisionPortalMember( } if err := access.Insert(ctx, tx, scope); err != nil { - return fmt.Errorf("cannot insert trust center access: %w", err) + return fmt.Errorf("cannot insert compliance page access: %w", err) } } diff --git a/pkg/complianceportal/visitor/third_party_service.go b/pkg/complianceportal/visitor/third_party_service.go index 1c9fc73e8..94f8d72e9 100644 --- a/pkg/complianceportal/visitor/third_party_service.go +++ b/pkg/complianceportal/visitor/third_party_service.go @@ -87,7 +87,7 @@ func (s *Service) ListThirdPartiesForOrganizationID( return page.NewPage(thirdParties, cursor), nil } -func (s *Service) ListDistinctTrustCenterCategoriesForOrganizationID( +func (s *Service) ListDistinctPortalCategoriesForOrganizationID( ctx context.Context, scope coredata.Scoper, organizationID gid.GID, @@ -116,7 +116,7 @@ func (s *Service) ListDistinctTrustCenterCategoriesForOrganizationID( return categories, nil } -func (s *Service) ListDistinctTrustCenterCountriesForOrganizationID( +func (s *Service) ListDistinctPortalCountriesForOrganizationID( ctx context.Context, scope coredata.Scoper, organizationID gid.GID, @@ -148,7 +148,7 @@ func (s *Service) ListDistinctTrustCenterCountriesForOrganizationID( func (s *Service) CountThirdPartiesForPortalID( ctx context.Context, scope coredata.Scoper, - trustCenterID gid.GID, + compliancePageID gid.GID, filter *coredata.ThirdPartyFilter, ) (int, error) { if filter == nil { @@ -161,14 +161,14 @@ func (s *Service) CountThirdPartiesForPortalID( err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) (err error) { - trustCenter, err := s.GetPortal(ctx, scope, trustCenterID) + compliancePage, err := s.GetPortal(ctx, scope, compliancePageID) if err != nil { - return fmt.Errorf("cannot load trust center: %w", err) + return fmt.Errorf("cannot load compliance page: %w", err) } thirdParties := &coredata.ThirdParties{} - count, err = thirdParties.CountByOrganizationID(ctx, conn, scope, trustCenter.OrganizationID, filter) + count, err = thirdParties.CountByOrganizationID(ctx, conn, scope, compliancePage.OrganizationID, filter) if err != nil { return fmt.Errorf("cannot count thirdParties: %w", err) } diff --git a/pkg/coredata/migrations/20260717T121103Z.sql b/pkg/coredata/migrations/20260717T121103Z.sql new file mode 100644 index 000000000..5eab3dfa3 --- /dev/null +++ b/pkg/coredata/migrations/20260717T121103Z.sql @@ -0,0 +1,21 @@ +-- Copyright (c) 2026 Probo Inc . +-- +-- 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. + +-- Append a random hex suffix to every compliance page slug so public URLs +-- are no longer guessable from the organization name. + +UPDATE trust_centers +SET + slug = slug || '-' || encode(gen_random_bytes(4), 'hex'), + updated_at = clock_timestamp(); diff --git a/pkg/iam/auth_service.go b/pkg/iam/auth_service.go index 29b17d455..ee3945a20 100644 --- a/pkg/iam/auth_service.go +++ b/pkg/iam/auth_service.go @@ -65,10 +65,6 @@ type ( Email mail.Addr URLPath string Continue *string - // If users tries to connect to compliance page, we must brand the emails accordingly - CompliancePageID *gid.GID - // OrganizationID brands compliance portal magic-link emails. - OrganizationID *gid.GID // OAuth2ClientIDRaw brands connect authorize magic-link emails. OAuth2ClientIDRaw *string MagicLinkBaseURL *string @@ -621,27 +617,10 @@ func (s AuthService) SendMagicLink(ctx context.Context, req *SendMagicLinkReques if branding != nil { senderName = branding.Name } - } else if req.OrganizationID != nil { - organization := &coredata.Organization{} - - if err := organization.LoadByID(ctx, tx, coredata.NewNoScope(), *req.OrganizationID); err != nil { - return fmt.Errorf("cannot load organization: %w", err) - } - - senderName = organization.Name } emailPresenterCfg := emails.DefaultPresenterConfig(s.baseURL) - if req.CompliancePageID != nil { - var err error - - emailPresenterCfg, err = s.CompliancePageService.EmailPresenterConfig(ctx, *req.CompliancePageID) - if err != nil { - return fmt.Errorf("cannot get compliance page email presenter config: %w", err) - } - } - if req.MagicLinkBaseURL != nil { emailPresenterCfg.BaseURL = *req.MagicLinkBaseURL } @@ -659,20 +638,13 @@ func (s AuthService) SendMagicLink(ctx context.Context, req *SendMagicLinkReques return fmt.Errorf("cannot render magic link email: %w", err) } - var emailOpts *coredata.EmailOptions - if req.CompliancePageID != nil { - emailOpts = &coredata.EmailOptions{ - SenderName: &senderName, - } - } - magicLinkEmail := coredata.NewEmail( fullName, req.Email, subject, textBody, htmlBody, - emailOpts, + nil, ) if err := magicLinkEmail.Insert(ctx, tx); err != nil { diff --git a/pkg/iam/compliance_page_service.go b/pkg/iam/compliance_page_service.go deleted file mode 100644 index 0b78a021f..000000000 --- a/pkg/iam/compliance_page_service.go +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright (c) 2026 Probo Inc . -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package iam - -import ( - "context" - "fmt" - "path/filepath" - "time" - - "go.gearno.de/kit/pg" - "go.probo.inc/probo/packages/emails" - "go.probo.inc/probo/pkg/complianceportal/resolver" - "go.probo.inc/probo/pkg/coredata" - "go.probo.inc/probo/pkg/gid" -) - -type ( - CompliancePageService struct { - *Service - } -) - -func NewCompliancePageService(svc *Service) *CompliancePageService { - return &CompliancePageService{Service: svc} -} - -func (s *CompliancePageService) GenerateLogoURL( - ctx context.Context, - compliancePageID gid.GID, - expiresIn time.Duration, -) (*string, error) { - file := &coredata.File{} - compliancePage := &coredata.TrustCenter{} - - scope := coredata.NewScopeFromObjectID(compliancePageID) - - err := s.pg.WithConn( - ctx, - func(ctx context.Context, conn pg.Querier) error { - if err := compliancePage.LoadByID(ctx, conn, scope, compliancePageID); err != nil { - return fmt.Errorf("cannot load compliance page: %w", err) - } - - if compliancePage.LogoFileID == nil { - return nil - } - - if err := file.LoadByID(ctx, conn, scope, *compliancePage.LogoFileID); err != nil { - return fmt.Errorf("cannot load file: %w", err) - } - - return nil - }, - ) - if err != nil { - return nil, err - } - - if compliancePage.LogoFileID == nil { - return nil, nil - } - - if file.FileKey == "" { - return nil, nil - } - - presignedURL, err := s.fm.GeneratePresignedURL(ctx, file, expiresIn) - if err != nil { - return nil, fmt.Errorf("cannot generate file URL: %w", err) - } - - return &presignedURL, nil -} - -func (s *CompliancePageService) EmailPresenterConfig(ctx context.Context, compliancePageID gid.GID) (emails.PresenterConfig, error) { - var ( - compliancePage = &coredata.TrustCenter{} - organization = &coredata.Organization{} - compliancePageURL string - logoFile = &coredata.File{} - emailPresenterCfg = emails.DefaultPresenterConfig(s.baseURL) - ) - - scope := coredata.NewScopeFromObjectID(compliancePageID) - - err := s.pg.WithConn( - ctx, - func(ctx context.Context, conn pg.Querier) error { - if err := compliancePage.LoadByID(ctx, conn, scope, compliancePageID); err != nil { - return fmt.Errorf("cannot load compliance page: %w", err) - } - - if compliancePage.LogoFileID != nil { - if err := logoFile.LoadByID(ctx, conn, scope, *compliancePage.LogoFileID); err != nil { - return fmt.Errorf("cannot load logoFile: %w", err) - } - } - - if err := organization.LoadByID(ctx, conn, scope, compliancePage.OrganizationID); err != nil { - return fmt.Errorf("cannot load organization: %w", err) - } - - publicURL, err := resolver.PublicURLForTrustCenter( - ctx, - conn, - scope, - compliancePage, - s.trustCenterBaseDomain, - ) - if err != nil { - return fmt.Errorf("cannot resolve compliance page URL: %w", err) - } - - compliancePageURL = publicURL - - return nil - }, - ) - if err != nil { - return emailPresenterCfg, err - } - - emailPresenterCfg.BaseURL = compliancePageURL - - if compliancePage.LogoFileID != nil { - if logoFile.FileKey == "" { - return emailPresenterCfg, nil - } - - // If logo exists, then we will brand the emails with the org as a sender - emailPresenterCfg.SenderCompanyLogoPath = filepath.Join("/api/files/v1/public/", logoFile.ID.String()) - emailPresenterCfg.SenderCompanyName = organization.Name - - if compliancePage.WebsiteURL != nil { - emailPresenterCfg.SenderCompanyWebsiteURL = *compliancePage.WebsiteURL - } - - if compliancePage.HeadquarterAddress != nil { - emailPresenterCfg.SenderCompanyHeadquarterAddress = *compliancePage.HeadquarterAddress - } - } - - return emailPresenterCfg, nil -} diff --git a/pkg/iam/organization_service.go b/pkg/iam/organization_service.go index e1ea3ebca..f11d7d0d0 100644 --- a/pkg/iam/organization_service.go +++ b/pkg/iam/organization_service.go @@ -613,7 +613,7 @@ func (s *OrganizationService) CreateOrganization( OrganizationID: organization.ID, TenantID: organization.TenantID, Active: false, - Slug: slug.Make(organization.Name), + Slug: slug.MakeWithEntropy(organization.Name), Title: organization.Name, SearchEngineIndexing: coredata.SearchEngineIndexingNotIndexable, MailingListID: &mailingList.ID, diff --git a/pkg/iam/service.go b/pkg/iam/service.go index f3428add3..b3cfc7f76 100644 --- a/pkg/iam/service.go +++ b/pkg/iam/service.go @@ -69,18 +69,17 @@ type ( privateKey *rsa.PrivateKey logger *log.Logger - AccountService *AccountService - OrganizationService *OrganizationService - CompliancePageService *CompliancePageService - SessionService *SessionService - AuthService *AuthService - SAMLService *saml.Service - OIDCService *oidc.Service - SCIMService *scim.Service - APIKeyService *APIKeyService - OAuth2ServerService *oauth2.Service - Authorizer *Authorizer - OAuth2ScopeRegistry *oauth2scope.Registry + AccountService *AccountService + OrganizationService *OrganizationService + SessionService *SessionService + AuthService *AuthService + SAMLService *saml.Service + OIDCService *oidc.Service + SCIMService *scim.Service + APIKeyService *APIKeyService + OAuth2ServerService *oauth2.Service + Authorizer *Authorizer + OAuth2ScopeRegistry *oauth2scope.Registry samlDomainVerifier *SAMLDomainVerifier } @@ -174,7 +173,6 @@ func NewService( svc.AccountService = NewAccountService(svc) svc.OrganizationService = NewOrganizationService(svc) - svc.CompliancePageService = NewCompliancePageService(svc) svc.SessionService = NewSessionService(svc) svc.AuthService = NewAuthService(svc) svc.APIKeyService = NewAPIKeyService(svc) diff --git a/pkg/mailman/compliance_mailing_list.go b/pkg/mailman/compliance_mailing_list.go index 268e6bf22..a43a3ebf8 100644 --- a/pkg/mailman/compliance_mailing_list.go +++ b/pkg/mailman/compliance_mailing_list.go @@ -29,7 +29,6 @@ import ( "go.gearno.de/kit/pg" "go.probo.inc/probo/packages/emails" "go.probo.inc/probo/pkg/baseurl" - "go.probo.inc/probo/pkg/complianceportal/resolver" "go.probo.inc/probo/pkg/coredata" "go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/mail" @@ -98,12 +97,11 @@ func (s *Service) mailingListEmailConfig( return fmt.Errorf("cannot load organization: %w", err) } - publicURL, err := resolver.PublicURLForTrustCenter( + publicURL, err := s.compliancePortal.PublicURLForCompliancePage( ctx, conn, scope, compliancePage, - s.trustCenterBaseDomain, ) if err != nil { return fmt.Errorf("cannot resolve compliance page URL: %w", err) diff --git a/pkg/mailman/service.go b/pkg/mailman/service.go index eb2f2600a..392c93840 100644 --- a/pkg/mailman/service.go +++ b/pkg/mailman/service.go @@ -30,6 +30,7 @@ import ( "go.gearno.de/kit/pg" "go.probo.inc/probo/packages/emails" "go.probo.inc/probo/pkg/baseurl" + "go.probo.inc/probo/pkg/complianceportal/management" "go.probo.inc/probo/pkg/coredata" "go.probo.inc/probo/pkg/crypto/cipher" "go.probo.inc/probo/pkg/filemanager" @@ -51,14 +52,14 @@ const ( ) type Service struct { - pg *pg.Client - fm *filemanager.Service - tokenSecret string - apiBaseURL *baseurl.BaseURL - trustCenterBaseDomain string - bucket string - encryptionKey cipher.EncryptionKey - logger *log.Logger + pg *pg.Client + fm *filemanager.Service + tokenSecret string + apiBaseURL *baseurl.BaseURL + compliancePortal *management.Service + bucket string + encryptionKey cipher.EncryptionKey + logger *log.Logger } func NewService( @@ -66,20 +67,20 @@ func NewService( fm *filemanager.Service, tokenSecret string, apiBaseURL *baseurl.BaseURL, - trustCenterBaseDomain string, + compliancePortal *management.Service, bucket string, encryptionKey cipher.EncryptionKey, logger *log.Logger, ) *Service { return &Service{ - pg: pgClient, - fm: fm, - tokenSecret: tokenSecret, - apiBaseURL: apiBaseURL, - trustCenterBaseDomain: trustCenterBaseDomain, - bucket: bucket, - encryptionKey: encryptionKey, - logger: logger, + pg: pgClient, + fm: fm, + tokenSecret: tokenSecret, + apiBaseURL: apiBaseURL, + compliancePortal: compliancePortal, + bucket: bucket, + encryptionKey: encryptionKey, + logger: logger, } } diff --git a/pkg/probod/probod.go b/pkg/probod/probod.go index f353ab1e2..b1e8b4d1b 100644 --- a/pkg/probod/probod.go +++ b/pkg/probod/probod.go @@ -52,7 +52,6 @@ import ( "go.probo.inc/probo/pkg/awsconfig" "go.probo.inc/probo/pkg/baseurl" "go.probo.inc/probo/pkg/certmanager" - "go.probo.inc/probo/pkg/complianceportal" "go.probo.inc/probo/pkg/complianceportal/management" "go.probo.inc/probo/pkg/complianceportal/visitor" "go.probo.inc/probo/pkg/connector" @@ -495,7 +494,7 @@ func (impl *Implm) Run( oauth2ScopeRegistry := oauth2scope.NewRegistry(). Register(iam.IAMOAuth2ScopeMappings). Register(probo.OAuth2ScopeMappings). - Register(complianceportal.OAuth2ScopeMappings). + Register(management.OAuth2ScopeMappings). Register(agentrun.OAuth2ScopeMappings). Register(accessreview.OAuth2ScopeMappings). Register(resourcealias.OAuth2ScopeMappings) @@ -618,12 +617,26 @@ func (impl *Implm) Run( l.Named("esign"), ) + resourceAliasService := resourcealias.NewService(pgClient) + + managementService := management.NewService( + pgClient, + s3Client, + impl.cfg.AWS.Bucket, + baseURL.String(), + impl.cfg.TrustCenter.BaseDomain, + fileManagerService, + certManagerService, + slackService, + l.Named("compliance-portal-management"), + ) + mailmanService := mailman.NewService( pgClient, fileManagerService, impl.cfg.Auth.Cookie.Secret, baseURL, - impl.cfg.TrustCenter.BaseDomain, + managementService, impl.cfg.AWS.Bucket, encryptionKey, l, @@ -658,20 +671,6 @@ func (impl *Implm) Run( return fmt.Errorf("cannot create probo service: %w", err) } - resourceAliasService := resourcealias.NewService(pgClient) - - managementService := management.NewService( - pgClient, - s3Client, - impl.cfg.AWS.Bucket, - baseURL.String(), - impl.cfg.TrustCenter.BaseDomain, - fileManagerService, - certManagerService, - slackService, - l.Named("compliance-portal-management"), - ) - trustService := visitor.NewService( pgClient, s3Client, @@ -686,6 +685,7 @@ func (impl *Implm) Run( l, slackService, resourceAliasService, + managementService, ) staticCIMDAllow := oauth2.CIMDAllowFromClientIDs(impl.cfg.Auth.OAuth2Server.CIMDAllowedClientIDs) @@ -716,7 +716,7 @@ func (impl *Implm) Run( iamService.Authorizer.RegisterPolicySet(agentrun.PolicySet()) iamService.Authorizer.RegisterPolicySet(accessreview.PolicySet()) iamService.Authorizer.RegisterPolicySet(resourcealias.PolicySet()) - iamService.Authorizer.RegisterPolicySet(complianceportal.PolicySet()) + iamService.Authorizer.RegisterPolicySet(management.PolicySet()) thirdPartyService := thirdparty.NewService(pgClient, fileManagerService, thirdPartyVetter) riskManagementService := riskmanagement.NewService(pgClient) @@ -732,6 +732,7 @@ func (impl *Implm) Run( Trust: trustService, ESign: esignService, Management: managementService, + CertManager: certManagerService, AccessReview: accessReviewService, AgentRun: agentRunService, Mailman: mailmanService, diff --git a/pkg/server/api/api.go b/pkg/server/api/api.go index 5cbfab174..4410e4821 100644 --- a/pkg/server/api/api.go +++ b/pkg/server/api/api.go @@ -34,6 +34,7 @@ import ( "go.probo.inc/probo/pkg/accessreview" "go.probo.inc/probo/pkg/agentrun" "go.probo.inc/probo/pkg/baseurl" + "go.probo.inc/probo/pkg/certmanager" "go.probo.inc/probo/pkg/complianceportal/management" "go.probo.inc/probo/pkg/complianceportal/visitor" "go.probo.inc/probo/pkg/connector" @@ -70,6 +71,7 @@ type ( Trust *visitor.Service ESign *esign.Service Management *management.Service + CertManager *certmanager.Service AccessReview *accessreview.Service AgentRun *agentrun.Service Slack *slack.Service @@ -192,6 +194,7 @@ func NewServer(cfg Config) (*Server, error) { cfg.IAM, cfg.ESign, cfg.Management, + cfg.CertManager, cfg.AccessReview, cfg.AgentRun, cfg.Mailman, @@ -225,6 +228,7 @@ func NewServer(cfg Config) (*Server, error) { cfg.Logger.Named("mcp.v1"), cfg.Probo, cfg.Management, + cfg.CertManager, cfg.ResourceAlias, cfg.ThirdParty, cfg.IAM, diff --git a/pkg/server/api/complianceportal/oauth.go b/pkg/server/api/complianceportal/oauth.go index 98ce4e141..ede557d52 100644 --- a/pkg/server/api/complianceportal/oauth.go +++ b/pkg/server/api/complianceportal/oauth.go @@ -15,23 +15,23 @@ package complianceportal import ( - portal "go.probo.inc/probo/pkg/complianceportal" + "go.probo.inc/probo/pkg/complianceportal/visitor" ) const ( - VisitorOAuthScope = portal.VisitorOAuthScope + VisitorOAuthScope = visitor.VisitorOAuthScope GraphQLPath = "/graphql" - CIMDMetadataPath = portal.CIMDMetadataPath - BrandLogoPath = portal.BrandLogoPath - BrandDarkLogoPath = portal.BrandDarkLogoPath + CIMDMetadataPath = visitor.CIMDMetadataPath + BrandLogoPath = visitor.BrandLogoPath + BrandDarkLogoPath = visitor.BrandDarkLogoPath OAuthInitiatePath = "/initiate" - OAuthCallbackPath = portal.OAuthCallbackPath + OAuthCallbackPath = visitor.OAuthCallbackPath ) func CIMDClientIDURL(portalBaseURL string) (string, error) { - return portal.CIMDClientIDURL(portalBaseURL) + return visitor.CIMDClientIDURL(portalBaseURL) } func OAuthCallbackURL(portalBaseURL string) (string, error) { - return portal.OAuthCallbackURL(portalBaseURL) + return visitor.OAuthCallbackURL(portalBaseURL) } diff --git a/pkg/server/api/complianceportal/v1/auth_resolvers.go b/pkg/server/api/complianceportal/v1/auth_resolvers.go index b4b2dec61..c657be49b 100644 --- a/pkg/server/api/complianceportal/v1/auth_resolvers.go +++ b/pkg/server/api/complianceportal/v1/auth_resolvers.go @@ -10,150 +10,14 @@ import ( "errors" "go.gearno.de/kit/log" - "go.probo.inc/probo/pkg/baseurl" "go.probo.inc/probo/pkg/coredata" "go.probo.inc/probo/pkg/iam" - "go.probo.inc/probo/pkg/saferedirect" "go.probo.inc/probo/pkg/server/api/authn" "go.probo.inc/probo/pkg/server/api/complianceportal" "go.probo.inc/probo/pkg/server/api/complianceportal/v1/types" "go.probo.inc/probo/pkg/server/gqlutils" ) -// SendMagicLink is the resolver for the sendMagicLink field. -func (r *mutationResolver) SendMagicLink(ctx context.Context, input types.SendMagicLinkInput) (*types.SendMagicLinkPayload, error) { - trustCenter := complianceportal.CompliancePageFromContext(ctx) - - baseURL := complianceportal.CompliancePageBaseURLFromContext(ctx) - - safeRedirect := saferedirect.New(saferedirect.StaticHosts(baseurl.MustParse(*baseURL).Host())) - - if input.Continue != nil { - _, ok := safeRedirect.Validate(ctx, *input.Continue) - if !ok { - return nil, gqlutils.Invalidf(ctx, "invalid continue URL") - } - } - - req := &iam.SendMagicLinkRequest{ - Email: input.Email, - CompliancePageID: &trustCenter.ID, - OrganizationID: &trustCenter.OrganizationID, - URLPath: "verify-magic-link", - Continue: input.Continue, - } - - if err := r.iam.AuthService.SendMagicLink(ctx, req); err != nil { - r.logger.ErrorCtx(ctx, "cannot send magic link", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return nil, nil -} - -// VerifyMagicLink is the resolver for the verifyMagicLink field. -func (r *mutationResolver) VerifyMagicLink(ctx context.Context, input types.VerifyMagicLinkInput) (*types.VerifyMagicLinkPayload, error) { - session := authn.SessionFromContext(ctx) - identity := authn.IdentityFromContext(ctx) - - email, err := r.iam.AuthService.GetMagicLinkEmail(ctx, input.Token) - if err != nil { - if _, ok := errors.AsType[*iam.ErrExpiredToken](err); ok { - return nil, gqlutils.TokenExpired(ctx, err) - } - - if _, ok := errors.AsType[*iam.ErrInvalidToken](err); ok { - return nil, gqlutils.Invalid(ctx, err) - } - - r.logger.ErrorCtx(ctx, "cannot get magic link email", log.Error(err)) - - return nil, gqlutils.Internal(ctx) - } - - var continueURL *string - - switch { - case session == nil: - var err error - - identity, session, continueURL, err = r.iam.AuthService.OpenSessionWithMagicLink(ctx, input.Token) - if err != nil { - if _, ok := errors.AsType[*iam.ErrExpiredToken](err); ok { - return nil, gqlutils.TokenExpired(ctx, err) - } - - if _, ok := errors.AsType[*iam.ErrTokenAlreadyUsed](err); ok { - return nil, gqlutils.TokenAlreadyUsed(ctx, err) - } - - if _, ok := errors.AsType[*iam.ErrInvalidToken](err); ok { - return nil, gqlutils.Invalid(ctx, err) - } - - r.logger.ErrorCtx(ctx, "cannot open session with magic link", log.Error(err)) - - return nil, gqlutils.Internal(ctx) - } - case identity.EmailAddress != email: - if err := r.iam.SessionService.CloseSession(ctx, session.ID); err != nil { - r.logger.ErrorCtx(ctx, "cannot close session", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - var err error - - identity, session, continueURL, err = r.iam.AuthService.OpenSessionWithMagicLink(ctx, input.Token) - if err != nil { - if _, ok := errors.AsType[*iam.ErrExpiredToken](err); ok { - return nil, gqlutils.TokenExpired(ctx, err) - } - - if _, ok := errors.AsType[*iam.ErrTokenAlreadyUsed](err); ok { - return nil, gqlutils.TokenAlreadyUsed(ctx, err) - } - - if _, ok := errors.AsType[*iam.ErrInvalidToken](err); ok { - return nil, gqlutils.Invalid(ctx, err) - } - - r.logger.ErrorCtx(ctx, "cannot open session with magic link", log.Error(err)) - - return nil, gqlutils.Internal(ctx) - } - } - - req := gqlutils.HTTPRequestFromContext(ctx) - if req == nil { - return nil, gqlutils.Internal(ctx) - } - - host, ok := complianceportal.TrustedRequestHost(req) - if !ok { - return nil, gqlutils.Internal(ctx) - } - - session.Data = coredata.SessionDataForHost(host) - if err := r.iam.SessionService.UpdateSessionData(ctx, session.ID, session.Data); err != nil { - r.logger.ErrorCtx(ctx, "cannot bind session to host", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - trustCenter := complianceportal.CompliancePageFromContext(ctx) - - if _, err := r.trust.ProvisionPortalMember(ctx, trustCenter.ID, identity.ID); err != nil { - r.logger.ErrorCtx(ctx, "cannot provision member", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - w := gqlutils.HTTPResponseWriterFromContext(ctx) - r.sessionCookie.Set(w, session) - - return &types.VerifyMagicLinkPayload{ - Continue: continueURL, - }, nil -} - // UpdateFullName is the resolver for the updateFullName field. func (r *mutationResolver) UpdateFullName(ctx context.Context, input types.UpdateFullNameInput) (*types.UpdateFullNamePayload, error) { identity := authn.IdentityFromContext(ctx) diff --git a/pkg/server/api/complianceportal/v1/base_resolvers.go b/pkg/server/api/complianceportal/v1/base_resolvers.go index d16624f6f..5eab20c4e 100644 --- a/pkg/server/api/complianceportal/v1/base_resolvers.go +++ b/pkg/server/api/complianceportal/v1/base_resolvers.go @@ -132,7 +132,7 @@ func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error trustCenterFile, err := trustService.GetPortalFile(ctx, scope, trustCenter.OrganizationID, id) if err != nil { - if errors.Is(err, visitor.ErrTrustCenterFileNotFound) || errors.Is(err, visitor.ErrTrustCenterFileNotVisible) { + if errors.Is(err, visitor.ErrPortalFileNotFound) || errors.Is(err, visitor.ErrPortalFileNotVisible) { return nil, gqlutils.NotFoundf(ctx, "node %q not found", id) } diff --git a/pkg/server/api/complianceportal/v1/graphql/auth.graphql b/pkg/server/api/complianceportal/v1/graphql/auth.graphql index d1ee32af0..cc0b4f5ca 100644 --- a/pkg/server/api/complianceportal/v1/graphql/auth.graphql +++ b/pkg/server/api/complianceportal/v1/graphql/auth.graphql @@ -1,30 +1,9 @@ extend type Mutation { - sendMagicLink(input: SendMagicLinkInput!): SendMagicLinkPayload - @authentication(required: OPTIONAL) - verifyMagicLink(input: VerifyMagicLinkInput!): VerifyMagicLinkPayload - @authentication(required: OPTIONAL) updateFullName(input: UpdateFullNameInput!): UpdateFullNamePayload @authentication(required: PRESENT) @sessionOnly signOut: SignOutPayload! @authentication(required: PRESENT) @sessionOnly } -input SendMagicLinkInput { - email: EmailAddr! - continue: String -} - -type SendMagicLinkPayload { - success: Boolean! -} - -input VerifyMagicLinkInput { - token: String! -} - -type VerifyMagicLinkPayload { - continue: String -} - input UpdateFullNameInput { fullName: String! } diff --git a/pkg/server/api/complianceportal/v1/mux.go b/pkg/server/api/complianceportal/v1/mux.go index 05a5a9650..19b00c192 100644 --- a/pkg/server/api/complianceportal/v1/mux.go +++ b/pkg/server/api/complianceportal/v1/mux.go @@ -22,7 +22,6 @@ import ( "go.gearno.de/kit/log" "go.gearno.de/x/ref" "go.probo.inc/probo/pkg/baseurl" - page "go.probo.inc/probo/pkg/complianceportal" visitor "go.probo.inc/probo/pkg/complianceportal/visitor" "go.probo.inc/probo/pkg/esign" "go.probo.inc/probo/pkg/filemanager" @@ -148,7 +147,7 @@ func compliancePageHeadData() HeadDataFunc { } if tc.LogoFileID != nil && compliancePageBaseURL != nil { - faviconURL, err := page.BrandLogoURL(*compliancePageBaseURL) + faviconURL, err := visitor.BrandLogoURL(*compliancePageBaseURL) if err == nil { headData.FaviconURL = faviconURL } diff --git a/pkg/server/api/complianceportal/v1/oauth_client_metadata_handler.go b/pkg/server/api/complianceportal/v1/oauth_client_metadata_handler.go index a97953880..1e7cdb1f0 100644 --- a/pkg/server/api/complianceportal/v1/oauth_client_metadata_handler.go +++ b/pkg/server/api/complianceportal/v1/oauth_client_metadata_handler.go @@ -19,7 +19,7 @@ import ( "net/http" "go.gearno.de/kit/httpserver" - portal "go.probo.inc/probo/pkg/complianceportal" + "go.probo.inc/probo/pkg/complianceportal/visitor" "go.probo.inc/probo/pkg/server/api/complianceportal" ) @@ -38,7 +38,7 @@ func (h *oauthClientMetadataHandler) ServeHTTP(w http.ResponseWriter, r *http.Re return } - doc, err := portal.BuildClientMetadataDocument(compliancePage, *baseURL) + doc, err := visitor.BuildClientMetadataDocument(compliancePage, *baseURL) if err != nil { httpserver.RenderError(w, http.StatusInternalServerError, errInternal) return diff --git a/pkg/server/api/complianceportal/v1/trust_center_resolvers.go b/pkg/server/api/complianceportal/v1/trust_center_resolvers.go index 202c482e2..0e54fabfa 100644 --- a/pkg/server/api/complianceportal/v1/trust_center_resolvers.go +++ b/pkg/server/api/complianceportal/v1/trust_center_resolvers.go @@ -467,7 +467,7 @@ func (r *mutationResolver) ExportTrustCenterFile(ctx context.Context, input type trustCenterFile, err := trustService.GetPortalFile(ctx, scope, trustCenter.OrganizationID, input.TrustCenterFileID) if err != nil { - if errors.Is(err, visitor.ErrTrustCenterFileNotFound) || errors.Is(err, visitor.ErrTrustCenterFileNotVisible) { + if errors.Is(err, visitor.ErrPortalFileNotFound) || errors.Is(err, visitor.ErrPortalFileNotVisible) { return nil, gqlutils.NotFoundf(ctx, "trust center file %q not found", input.TrustCenterFileID) } @@ -620,7 +620,7 @@ func (r *mutationResolver) RequestTrustCenterFileAccess(ctx context.Context, inp trustCenterFile, err := trustService.GetPortalFile(ctx, scope, trustCenter.OrganizationID, input.TrustCenterFileID) if err != nil { - if errors.Is(err, visitor.ErrTrustCenterFileNotFound) || errors.Is(err, visitor.ErrTrustCenterFileNotVisible) { + if errors.Is(err, visitor.ErrPortalFileNotFound) || errors.Is(err, visitor.ErrPortalFileNotVisible) { return nil, gqlutils.NotFoundf(ctx, "trust center file %q not found", input.TrustCenterFileID) } @@ -836,7 +836,7 @@ func (r *trustCenterResolver) SubprocessorCategories(ctx context.Context, obj *t trustCenter := complianceportal.CompliancePageFromContext(ctx) scope := coredata.NewScopeFromObjectID(obj.ID) - categories, err := r.trust.ListDistinctTrustCenterCategoriesForOrganizationID(ctx, scope, trustCenter.OrganizationID) + categories, err := r.trust.ListDistinctPortalCategoriesForOrganizationID(ctx, scope, trustCenter.OrganizationID) if err != nil { r.logger.ErrorCtx(ctx, "cannot list subprocessor categories", log.Error(err)) return nil, gqlutils.Internal(ctx) @@ -850,7 +850,7 @@ func (r *trustCenterResolver) SubprocessorCountries(ctx context.Context, obj *ty trustCenter := complianceportal.CompliancePageFromContext(ctx) scope := coredata.NewScopeFromObjectID(obj.ID) - countries, err := r.trust.ListDistinctTrustCenterCountriesForOrganizationID(ctx, scope, trustCenter.OrganizationID) + countries, err := r.trust.ListDistinctPortalCountriesForOrganizationID(ctx, scope, trustCenter.OrganizationID) if err != nil { r.logger.ErrorCtx(ctx, "cannot list subprocessor countries", log.Error(err)) return nil, gqlutils.Internal(ctx) @@ -986,7 +986,7 @@ func (r *trustCenterFileResolver) IsUserAuthorized(ctx context.Context, obj *typ trustCenterFile, err := trustService.GetPortalFile(ctx, scope, trustCenter.OrganizationID, obj.ID) if err != nil { - if errors.Is(err, visitor.ErrTrustCenterFileNotFound) || errors.Is(err, visitor.ErrTrustCenterFileNotVisible) { + if errors.Is(err, visitor.ErrPortalFileNotFound) || errors.Is(err, visitor.ErrPortalFileNotVisible) { return false, gqlutils.NotFoundf(ctx, "trust center file %q not found", obj.ID) } diff --git a/pkg/server/api/console/v1/base_resolvers.go b/pkg/server/api/console/v1/base_resolvers.go index 066d3c825..8345b4d47 100644 --- a/pkg/server/api/console/v1/base_resolvers.go +++ b/pkg/server/api/console/v1/base_resolvers.go @@ -14,7 +14,7 @@ import ( "go.gearno.de/kit/log" "go.probo.inc/probo/pkg/accessreview" "go.probo.inc/probo/pkg/agentrun" - "go.probo.inc/probo/pkg/complianceportal" + "go.probo.inc/probo/pkg/complianceportal/management" "go.probo.inc/probo/pkg/coredata" "go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/probo" @@ -335,7 +335,7 @@ func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error return types.NewTransferImpactAssessment(tia), nil } case coredata.TrustCenterEntityType: - action = complianceportal.ActionCompliancePortalGet + action = management.ActionCompliancePortalGet loadNode = func(ctx context.Context, scope *coredata.Scope, id gid.GID) (types.Node, error) { trustCenter, err := r.management.Get(ctx, scope, id) if err != nil { @@ -345,7 +345,7 @@ func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error return types.NewTrustCenter(trustCenter), nil } case coredata.TrustCenterAccessEntityType: - action = complianceportal.ActionCompliancePortalAccessGet + action = management.ActionCompliancePortalAccessGet loadNode = func(ctx context.Context, scope *coredata.Scope, id gid.GID) (types.Node, error) { trustCenterAccess, err := r.management.GetAccess(ctx, scope, id) if err != nil { diff --git a/pkg/server/api/console/v1/graphql_handler.go b/pkg/server/api/console/v1/graphql_handler.go index 334e7201d..f3d240d5d 100644 --- a/pkg/server/api/console/v1/graphql_handler.go +++ b/pkg/server/api/console/v1/graphql_handler.go @@ -27,6 +27,7 @@ import ( "go.probo.inc/probo/pkg/accessreview" "go.probo.inc/probo/pkg/agentrun" "go.probo.inc/probo/pkg/baseurl" + "go.probo.inc/probo/pkg/certmanager" "go.probo.inc/probo/pkg/complianceportal/management" "go.probo.inc/probo/pkg/connector" "go.probo.inc/probo/pkg/connector/provider" @@ -51,6 +52,7 @@ func NewGraphQLHandler( resourceAliasSvc *resourcealias.Service, esignSvc *esign.Service, managementSvc *management.Service, + certManagerSvc *certmanager.Service, accessReviewSvc *accessreview.Service, agentRunSvc *agentrun.Service, mailmanSvc *mailman.Service, @@ -75,6 +77,7 @@ func NewGraphQLHandler( iam: iamSvc, esign: esignSvc, management: managementSvc, + certManager: certManagerSvc, accessReview: accessReviewSvc, agentRun: agentRunSvc, mailman: mailmanSvc, diff --git a/pkg/server/api/console/v1/mailing_list_resolvers.go b/pkg/server/api/console/v1/mailing_list_resolvers.go index 4c3af4132..cb500cc0a 100644 --- a/pkg/server/api/console/v1/mailing_list_resolvers.go +++ b/pkg/server/api/console/v1/mailing_list_resolvers.go @@ -11,7 +11,7 @@ import ( "fmt" "go.gearno.de/kit/log" - "go.probo.inc/probo/pkg/complianceportal" + "go.probo.inc/probo/pkg/complianceportal/management" "go.probo.inc/probo/pkg/coredata" "go.probo.inc/probo/pkg/mailman" "go.probo.inc/probo/pkg/page" @@ -23,7 +23,7 @@ import ( // Subscribers is the resolver for the subscribers field on MailingList. func (r *mailingListResolver) Subscribers(ctx context.Context, obj *types.MailingList, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.MailingListSubscriberConnection, error) { - if _, err := r.authorize(ctx, obj.ID, complianceportal.ActionMailingListSubscriberList); err != nil { + if _, err := r.authorize(ctx, obj.ID, management.ActionMailingListSubscriberList); err != nil { return nil, err } @@ -45,7 +45,7 @@ func (r *mailingListResolver) Subscribers(ctx context.Context, obj *types.Mailin // Updates is the resolver for the updates field on MailingList. func (r *mailingListResolver) Updates(ctx context.Context, obj *types.MailingList, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.MailingListUpdateConnection, error) { - if _, err := r.authorize(ctx, obj.ID, complianceportal.ActionMailingListUpdateList); err != nil { + if _, err := r.authorize(ctx, obj.ID, management.ActionMailingListUpdateList); err != nil { return nil, err } @@ -67,7 +67,7 @@ func (r *mailingListResolver) Updates(ctx context.Context, obj *types.MailingLis // TotalCount is the resolver for the totalCount field. func (r *mailingListSubscriberConnectionResolver) TotalCount(ctx context.Context, obj *types.MailingListSubscriberConnection) (int, error) { - if _, err := r.authorize(ctx, obj.ParentID, complianceportal.ActionMailingListSubscriberList); err != nil { + if _, err := r.authorize(ctx, obj.ParentID, management.ActionMailingListSubscriberList); err != nil { return 0, err } @@ -89,7 +89,7 @@ func (r *mailingListSubscriberConnectionResolver) TotalCount(ctx context.Context // TotalCount is the resolver for the totalCount field on MailingListUpdateConnection. func (r *mailingListUpdateConnectionResolver) TotalCount(ctx context.Context, obj *types.MailingListUpdateConnection) (int, error) { - if _, err := r.authorize(ctx, obj.ParentID, complianceportal.ActionMailingListUpdateList); err != nil { + if _, err := r.authorize(ctx, obj.ParentID, management.ActionMailingListUpdateList); err != nil { return 0, err } @@ -104,7 +104,7 @@ func (r *mailingListUpdateConnectionResolver) TotalCount(ctx context.Context, ob // CreateMailingListUpdate is the resolver for the createMailingListUpdate field. func (r *mutationResolver) CreateMailingListUpdate(ctx context.Context, input types.CreateMailingListUpdateInput) (*types.CreateMailingListUpdatePayload, error) { - if _, err := r.authorize(ctx, input.MailingListID, complianceportal.ActionMailingListUpdateCreate); err != nil { + if _, err := r.authorize(ctx, input.MailingListID, management.ActionMailingListUpdateCreate); err != nil { return nil, err } @@ -133,7 +133,7 @@ func (r *mutationResolver) CreateMailingListUpdate(ctx context.Context, input ty // UpdateMailingListUpdate is the resolver for the updateMailingListUpdate field. func (r *mutationResolver) UpdateMailingListUpdate(ctx context.Context, input types.UpdateMailingListUpdateInput) (*types.UpdateMailingListUpdatePayload, error) { - if _, err := r.authorize(ctx, input.ID, complianceportal.ActionMailingListUpdateUpdate); err != nil { + if _, err := r.authorize(ctx, input.ID, management.ActionMailingListUpdateUpdate); err != nil { return nil, err } @@ -170,7 +170,7 @@ func (r *mutationResolver) UpdateMailingListUpdate(ctx context.Context, input ty // SendMailingListUpdate is the resolver for the sendMailingListUpdate field. func (r *mutationResolver) SendMailingListUpdate(ctx context.Context, input types.SendMailingListUpdateInput) (*types.SendMailingListUpdatePayload, error) { - if _, err := r.authorize(ctx, input.ID, complianceportal.ActionMailingListUpdateUpdate); err != nil { + if _, err := r.authorize(ctx, input.ID, management.ActionMailingListUpdateUpdate); err != nil { return nil, err } @@ -196,7 +196,7 @@ func (r *mutationResolver) SendMailingListUpdate(ctx context.Context, input type // DeleteMailingListUpdate is the resolver for the deleteMailingListUpdate field. func (r *mutationResolver) DeleteMailingListUpdate(ctx context.Context, input types.DeleteMailingListUpdateInput) (*types.DeleteMailingListUpdatePayload, error) { - if _, err := r.authorize(ctx, input.ID, complianceportal.ActionMailingListUpdateDelete); err != nil { + if _, err := r.authorize(ctx, input.ID, management.ActionMailingListUpdateDelete); err != nil { return nil, err } @@ -217,7 +217,7 @@ func (r *mutationResolver) DeleteMailingListUpdate(ctx context.Context, input ty // UpdateMailingList is the resolver for the updateMailingList field. func (r *mutationResolver) UpdateMailingList(ctx context.Context, input types.UpdateMailingListInput) (*types.UpdateMailingListPayload, error) { - if _, err := r.authorize(ctx, input.ID, complianceportal.ActionMailingListUpdate); err != nil { + if _, err := r.authorize(ctx, input.ID, management.ActionMailingListUpdate); err != nil { return nil, err } @@ -234,7 +234,7 @@ func (r *mutationResolver) UpdateMailingList(ctx context.Context, input types.Up // CreateMailingListSubscriber is the resolver for the createMailingListSubscriber field. func (r *mutationResolver) CreateMailingListSubscriber(ctx context.Context, input types.CreateMailingListSubscriberInput) (*types.CreateMailingListSubscriberPayload, error) { - if _, err := r.authorize(ctx, input.MailingListID, complianceportal.ActionMailingListSubscriberCreate); err != nil { + if _, err := r.authorize(ctx, input.MailingListID, management.ActionMailingListSubscriberCreate); err != nil { return nil, err } @@ -268,7 +268,7 @@ func (r *mutationResolver) CreateMailingListSubscriber(ctx context.Context, inpu // DeleteMailingListSubscriber is the resolver for the deleteMailingListSubscriber field. func (r *mutationResolver) DeleteMailingListSubscriber(ctx context.Context, input types.DeleteMailingListSubscriberInput) (*types.DeleteMailingListSubscriberPayload, error) { - if _, err := r.authorize(ctx, input.ID, complianceportal.ActionMailingListSubscriberDelete); err != nil { + if _, err := r.authorize(ctx, input.ID, management.ActionMailingListSubscriberDelete); err != nil { return nil, err } diff --git a/pkg/server/api/console/v1/organization_resolvers.go b/pkg/server/api/console/v1/organization_resolvers.go index 9626327fa..322c1ef0b 100644 --- a/pkg/server/api/console/v1/organization_resolvers.go +++ b/pkg/server/api/console/v1/organization_resolvers.go @@ -13,7 +13,7 @@ import ( "go.gearno.de/kit/log" "go.probo.inc/probo/pkg/accessreview" "go.probo.inc/probo/pkg/agentrun" - "go.probo.inc/probo/pkg/complianceportal" + "go.probo.inc/probo/pkg/complianceportal/management" "go.probo.inc/probo/pkg/coredata" "go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/iam" @@ -1159,7 +1159,7 @@ func (r *organizationResolver) AgentRuns(ctx context.Context, obj *types.Organiz // TrustCenter is the resolver for the trustCenter field. func (r *organizationResolver) TrustCenter(ctx context.Context, obj *types.Organization) (*types.TrustCenter, error) { - scope, err := r.authorize(ctx, obj.ID, complianceportal.ActionCompliancePortalGet) + scope, err := r.authorize(ctx, obj.ID, management.ActionCompliancePortalGet) if err != nil { return nil, err } @@ -1175,7 +1175,7 @@ func (r *organizationResolver) TrustCenter(ctx context.Context, obj *types.Organ // TrustCenterFiles is the resolver for the trustCenterFiles field. func (r *organizationResolver) TrustCenterFiles(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.OrderBy[coredata.TrustCenterFileOrderField]) (*types.TrustCenterFileConnection, error) { - scope, err := r.authorize(ctx, obj.ID, complianceportal.ActionCompliancePortalFileList) + scope, err := r.authorize(ctx, obj.ID, management.ActionCompliancePortalFileList) if err != nil { return nil, err } diff --git a/pkg/server/api/console/v1/resolver.go b/pkg/server/api/console/v1/resolver.go index b1f5f2e39..3524b1249 100644 --- a/pkg/server/api/console/v1/resolver.go +++ b/pkg/server/api/console/v1/resolver.go @@ -35,6 +35,7 @@ import ( "go.probo.inc/probo/pkg/accessreview" "go.probo.inc/probo/pkg/agentrun" "go.probo.inc/probo/pkg/baseurl" + "go.probo.inc/probo/pkg/certmanager" "go.probo.inc/probo/pkg/complianceportal/management" "go.probo.inc/probo/pkg/connector" "go.probo.inc/probo/pkg/connector/provider" @@ -67,6 +68,7 @@ type ( iam *iam.Service esign *esign.Service management *management.Service + certManager *certmanager.Service accessReview *accessreview.Service agentRun *agentrun.Service mailman *mailman.Service @@ -90,10 +92,14 @@ func (r *Resolver) newCustomDomainType( scope coredata.Scoper, domain *coredata.CustomDomain, ) (*types.CustomDomain, error) { - cert, err := r.management.GetCertificate(ctx, scope, domain) - if err != nil { - r.logger.ErrorCtx(ctx, "cannot load certificate", log.Error(err)) - return nil, gqlutils.Internal(ctx) + var cert *coredata.Certificate + if domain != nil && domain.CertificateID != nil { + var err error + cert, err = r.certManager.Get(ctx, scope, *domain.CertificateID) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot load certificate", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } } return types.NewCustomDomain(domain, cert, r.customDomainCname), nil @@ -106,6 +112,7 @@ func NewMux( iamSvc *iam.Service, esignSvc *esign.Service, managementSvc *management.Service, + certManagerSvc *certmanager.Service, accessReviewSvc *accessreview.Service, agentRunSvc *agentrun.Service, mailmanSvc *mailman.Service, @@ -131,6 +138,7 @@ func NewMux( resourceAliasSvc, esignSvc, managementSvc, + certManagerSvc, accessReviewSvc, agentRunSvc, mailmanSvc, diff --git a/pkg/server/api/console/v1/trust_center_resolvers.go b/pkg/server/api/console/v1/trust_center_resolvers.go index 9b702511b..a68f66fc7 100644 --- a/pkg/server/api/console/v1/trust_center_resolvers.go +++ b/pkg/server/api/console/v1/trust_center_resolvers.go @@ -12,7 +12,6 @@ import ( "github.com/vikstrous/dataloadgen" "go.gearno.de/kit/log" - "go.probo.inc/probo/pkg/complianceportal" "go.probo.inc/probo/pkg/complianceportal/management" "go.probo.inc/probo/pkg/coredata" "go.probo.inc/probo/pkg/iam" @@ -60,7 +59,7 @@ func (r *customDomainResolver) Permission(ctx context.Context, obj *types.Custom // UpdateTrustCenter is the resolver for the updateTrustCenter field. func (r *mutationResolver) UpdateTrustCenter(ctx context.Context, input types.UpdateTrustCenterInput) (*types.UpdateTrustCenterPayload, error) { - scope, err := r.authorize(ctx, input.TrustCenterID, complianceportal.ActionCompliancePortalUpdate) + scope, err := r.authorize(ctx, input.TrustCenterID, management.ActionCompliancePortalUpdate) if err != nil { return nil, err } @@ -95,7 +94,7 @@ func (r *mutationResolver) UpdateTrustCenter(ctx context.Context, input types.Up // UploadTrustCenterNda is the resolver for the uploadTrustCenterNDA field. func (r *mutationResolver) UploadTrustCenterNda(ctx context.Context, input types.UploadTrustCenterNDAInput) (*types.UploadTrustCenterNDAPayload, error) { - scope, err := r.authorize(ctx, input.TrustCenterID, complianceportal.ActionCompliancePortalNonDisclosureAgreementUpload) + scope, err := r.authorize(ctx, input.TrustCenterID, management.ActionCompliancePortalNonDisclosureAgreementUpload) if err != nil { return nil, err } @@ -125,7 +124,7 @@ func (r *mutationResolver) UploadTrustCenterNda(ctx context.Context, input types // DeleteTrustCenterNda is the resolver for the deleteTrustCenterNDA field. func (r *mutationResolver) DeleteTrustCenterNda(ctx context.Context, input types.DeleteTrustCenterNDAInput) (*types.DeleteTrustCenterNDAPayload, error) { - scope, err := r.authorize(ctx, input.TrustCenterID, complianceportal.ActionCompliancePortalNonDisclosureAgreementDelete) + scope, err := r.authorize(ctx, input.TrustCenterID, management.ActionCompliancePortalNonDisclosureAgreementDelete) if err != nil { return nil, err } @@ -143,7 +142,7 @@ func (r *mutationResolver) DeleteTrustCenterNda(ctx context.Context, input types // UpdateTrustCenterBrand is the resolver for the updateTrustCenterBrand field. func (r *mutationResolver) UpdateTrustCenterBrand(ctx context.Context, input types.UpdateTrustCenterBrandInput) (*types.UpdateTrustCenterBrandPayload, error) { - scope, err := r.authorize(ctx, input.TrustCenterID, complianceportal.ActionCompliancePortalUpdate) + scope, err := r.authorize(ctx, input.TrustCenterID, management.ActionCompliancePortalUpdate) if err != nil { return nil, err } @@ -204,7 +203,7 @@ func (r *mutationResolver) UpdateTrustCenterBrand(ctx context.Context, input typ // UpdateTrustCenterAccess is the resolver for the updateTrustCenterAccess field. func (r *mutationResolver) UpdateTrustCenterAccess(ctx context.Context, input types.UpdateTrustCenterAccessInput) (*types.UpdateTrustCenterAccessPayload, error) { - scope, err := r.authorize(ctx, input.ID, complianceportal.ActionCompliancePortalAccessUpdate) + scope, err := r.authorize(ctx, input.ID, management.ActionCompliancePortalAccessUpdate) if err != nil { return nil, err } @@ -262,7 +261,7 @@ func (r *mutationResolver) UpdateTrustCenterAccess(ctx context.Context, input ty // DeleteTrustCenterAccess is the resolver for the deleteTrustCenterAccess field. func (r *mutationResolver) DeleteTrustCenterAccess(ctx context.Context, input types.DeleteTrustCenterAccessInput) (*types.DeleteTrustCenterAccessPayload, error) { - scope, err := r.authorize(ctx, input.ID, complianceportal.ActionCompliancePortalAccessDelete) + scope, err := r.authorize(ctx, input.ID, management.ActionCompliancePortalAccessDelete) if err != nil { return nil, err } @@ -279,7 +278,7 @@ func (r *mutationResolver) DeleteTrustCenterAccess(ctx context.Context, input ty // CreateTrustCenterReference is the resolver for the createTrustCenterReference field. func (r *mutationResolver) CreateTrustCenterReference(ctx context.Context, input types.CreateTrustCenterReferenceInput) (*types.CreateTrustCenterReferencePayload, error) { - scope, err := r.authorize(ctx, input.TrustCenterID, complianceportal.ActionCompliancePortalReferenceCreate) + scope, err := r.authorize(ctx, input.TrustCenterID, management.ActionCompliancePortalReferenceCreate) if err != nil { return nil, err } @@ -316,7 +315,7 @@ func (r *mutationResolver) CreateTrustCenterReference(ctx context.Context, input // UpdateTrustCenterReference is the resolver for the updateTrustCenterReference field. func (r *mutationResolver) UpdateTrustCenterReference(ctx context.Context, input types.UpdateTrustCenterReferenceInput) (*types.UpdateTrustCenterReferencePayload, error) { - scope, err := r.authorize(ctx, input.ID, complianceportal.ActionCompliancePortalReferenceUpdate) + scope, err := r.authorize(ctx, input.ID, management.ActionCompliancePortalReferenceUpdate) if err != nil { return nil, err } @@ -356,7 +355,7 @@ func (r *mutationResolver) UpdateTrustCenterReference(ctx context.Context, input // DeleteTrustCenterReference is the resolver for the deleteTrustCenterReference field. func (r *mutationResolver) DeleteTrustCenterReference(ctx context.Context, input types.DeleteTrustCenterReferenceInput) (*types.DeleteTrustCenterReferencePayload, error) { - scope, err := r.authorize(ctx, input.ID, complianceportal.ActionCompliancePortalReferenceDelete) + scope, err := r.authorize(ctx, input.ID, management.ActionCompliancePortalReferenceDelete) if err != nil { return nil, err } @@ -373,7 +372,7 @@ func (r *mutationResolver) DeleteTrustCenterReference(ctx context.Context, input // CreateComplianceFramework is the resolver for the createComplianceFramework field. func (r *mutationResolver) CreateComplianceFramework(ctx context.Context, input types.CreateComplianceFrameworkInput) (*types.CreateComplianceFrameworkPayload, error) { - scope, err := r.authorize(ctx, input.TrustCenterID, complianceportal.ActionComplianceFrameworkCreate) + scope, err := r.authorize(ctx, input.TrustCenterID, management.ActionComplianceFrameworkCreate) if err != nil { return nil, err } @@ -402,7 +401,7 @@ func (r *mutationResolver) CreateComplianceFramework(ctx context.Context, input // UpdateComplianceFramework is the resolver for the updateComplianceFramework field. func (r *mutationResolver) UpdateComplianceFramework(ctx context.Context, input types.UpdateComplianceFrameworkInput) (*types.UpdateComplianceFrameworkPayload, error) { - scope, err := r.authorize(ctx, input.ID, complianceportal.ActionComplianceFrameworkUpdateRank) + scope, err := r.authorize(ctx, input.ID, management.ActionComplianceFrameworkUpdateRank) if err != nil { return nil, err } @@ -428,7 +427,7 @@ func (r *mutationResolver) UpdateComplianceFramework(ctx context.Context, input // DeleteComplianceFramework is the resolver for the deleteComplianceFramework field. func (r *mutationResolver) DeleteComplianceFramework(ctx context.Context, input types.DeleteComplianceFrameworkInput) (*types.DeleteComplianceFrameworkPayload, error) { - scope, err := r.authorize(ctx, input.ID, complianceportal.ActionComplianceFrameworkDelete) + scope, err := r.authorize(ctx, input.ID, management.ActionComplianceFrameworkDelete) if err != nil { return nil, err } @@ -455,7 +454,7 @@ func (r *mutationResolver) DeleteComplianceFramework(ctx context.Context, input // CreateComplianceCustomLink is the resolver for the createComplianceCustomLink field. func (r *mutationResolver) CreateComplianceCustomLink(ctx context.Context, input types.CreateComplianceCustomLinkInput) (*types.CreateComplianceCustomLinkPayload, error) { - scope, err := r.authorize(ctx, input.TrustCenterID, complianceportal.ActionComplianceCustomLinkCreate) + scope, err := r.authorize(ctx, input.TrustCenterID, management.ActionComplianceCustomLinkCreate) if err != nil { return nil, err } @@ -485,7 +484,7 @@ func (r *mutationResolver) CreateComplianceCustomLink(ctx context.Context, input // UpdateComplianceCustomLink is the resolver for the updateComplianceCustomLink field. func (r *mutationResolver) UpdateComplianceCustomLink(ctx context.Context, input types.UpdateComplianceCustomLinkInput) (*types.UpdateComplianceCustomLinkPayload, error) { - scope, err := r.authorize(ctx, input.ID, complianceportal.ActionComplianceCustomLinkUpdate) + scope, err := r.authorize(ctx, input.ID, management.ActionComplianceCustomLinkUpdate) if err != nil { return nil, err } @@ -513,7 +512,7 @@ func (r *mutationResolver) UpdateComplianceCustomLink(ctx context.Context, input // DeleteComplianceCustomLink is the resolver for the deleteComplianceCustomLink field. func (r *mutationResolver) DeleteComplianceCustomLink(ctx context.Context, input types.DeleteComplianceCustomLinkInput) (*types.DeleteComplianceCustomLinkPayload, error) { - scope, err := r.authorize(ctx, input.ID, complianceportal.ActionComplianceCustomLinkDelete) + scope, err := r.authorize(ctx, input.ID, management.ActionComplianceCustomLinkDelete) if err != nil { return nil, err } @@ -535,7 +534,7 @@ func (r *mutationResolver) DeleteComplianceCustomLink(ctx context.Context, input // CreateTrustCenterFile is the resolver for the createTrustCenterFile field. func (r *mutationResolver) CreateTrustCenterFile(ctx context.Context, input types.CreateTrustCenterFileInput) (*types.CreateTrustCenterFilePayload, error) { - scope, err := r.authorize(ctx, input.OrganizationID, complianceportal.ActionCompliancePortalFileCreate) + scope, err := r.authorize(ctx, input.OrganizationID, management.ActionCompliancePortalFileCreate) if err != nil { return nil, err } @@ -572,7 +571,7 @@ func (r *mutationResolver) CreateTrustCenterFile(ctx context.Context, input type // UpdateTrustCenterFile is the resolver for the updateTrustCenterFile field. func (r *mutationResolver) UpdateTrustCenterFile(ctx context.Context, input types.UpdateTrustCenterFileInput) (*types.UpdateTrustCenterFilePayload, error) { - scope, err := r.authorize(ctx, input.ID, complianceportal.ActionCompliancePortalFileUpdate) + scope, err := r.authorize(ctx, input.ID, management.ActionCompliancePortalFileUpdate) if err != nil { return nil, err } @@ -603,7 +602,7 @@ func (r *mutationResolver) UpdateTrustCenterFile(ctx context.Context, input type // GetTrustCenterFile is the resolver for the getTrustCenterFile field. func (r *mutationResolver) GetTrustCenterFile(ctx context.Context, input types.GetTrustCenterFileInput) (*types.GetTrustCenterFilePayload, error) { - scope, err := r.authorize(ctx, input.ID, complianceportal.ActionCompliancePortalFileGet) + scope, err := r.authorize(ctx, input.ID, management.ActionCompliancePortalFileGet) if err != nil { return nil, err } @@ -621,7 +620,7 @@ func (r *mutationResolver) GetTrustCenterFile(ctx context.Context, input types.G // DeleteTrustCenterFile is the resolver for the deleteTrustCenterFile field. func (r *mutationResolver) DeleteTrustCenterFile(ctx context.Context, input types.DeleteTrustCenterFileInput) (*types.DeleteTrustCenterFilePayload, error) { - scope, err := r.authorize(ctx, input.ID, complianceportal.ActionCompliancePortalFileDelete) + scope, err := r.authorize(ctx, input.ID, management.ActionCompliancePortalFileDelete) if err != nil { return nil, err } @@ -638,7 +637,7 @@ func (r *mutationResolver) DeleteTrustCenterFile(ctx context.Context, input type // CreateCustomDomain is the resolver for the createCustomDomain field. func (r *mutationResolver) CreateCustomDomain(ctx context.Context, input types.CreateCustomDomainInput) (*types.CreateCustomDomainPayload, error) { - scope, err := r.authorize(ctx, input.TrustCenterID, complianceportal.ActionCustomDomainCreate) + scope, err := r.authorize(ctx, input.TrustCenterID, management.ActionCustomDomainCreate) if err != nil { return nil, err } @@ -674,13 +673,13 @@ func (r *mutationResolver) CreateCustomDomain(ctx context.Context, input types.C // DeleteCustomDomain is the resolver for the deleteCustomDomain field. func (r *mutationResolver) DeleteCustomDomain(ctx context.Context, input types.DeleteCustomDomainInput) (*types.DeleteCustomDomainPayload, error) { - scope, err := r.authorize(ctx, input.CustomDomainID, complianceportal.ActionCustomDomainDelete) + scope, err := r.authorize(ctx, input.CustomDomainID, management.ActionCustomDomainDelete) if err != nil { return nil, err } if err := r.management.RemoveCustomDomain(ctx, scope, input.CustomDomainID); err != nil { - if errors.Is(err, complianceportal.ErrCustomDomainManaged) { + if errors.Is(err, management.ErrCustomDomainManaged) { return nil, gqlutils.Conflictf(ctx, "managed domain cannot be deleted") } @@ -696,7 +695,7 @@ func (r *mutationResolver) DeleteCustomDomain(ctx context.Context, input types.D // Logo is the resolver for the logo field. func (r *trustCenterResolver) Logo(ctx context.Context, obj *types.TrustCenter) (*types.File, error) { - if _, err := r.authorize(ctx, obj.ID, complianceportal.ActionCompliancePortalGet); err != nil { + if _, err := r.authorize(ctx, obj.ID, management.ActionCompliancePortalGet); err != nil { return nil, err } @@ -709,7 +708,7 @@ func (r *trustCenterResolver) Logo(ctx context.Context, obj *types.TrustCenter) // DarkLogo is the resolver for the darkLogo field. func (r *trustCenterResolver) DarkLogo(ctx context.Context, obj *types.TrustCenter) (*types.File, error) { - if _, err := r.authorize(ctx, obj.ID, complianceportal.ActionCompliancePortalGet); err != nil { + if _, err := r.authorize(ctx, obj.ID, management.ActionCompliancePortalGet); err != nil { return nil, err } @@ -722,7 +721,7 @@ func (r *trustCenterResolver) DarkLogo(ctx context.Context, obj *types.TrustCent // Nda is the resolver for the nda field. func (r *trustCenterResolver) Nda(ctx context.Context, obj *types.TrustCenter) (*types.File, error) { - hasPermission, err := r.Resolver.Permission(ctx, obj, complianceportal.ActionCompliancePortalGetNda) + hasPermission, err := r.Resolver.Permission(ctx, obj, management.ActionCompliancePortalGetNda) if err != nil { r.logger.ErrorCtx(ctx, "cannot authorize", log.Error(err)) return nil, gqlutils.Internal(ctx) @@ -764,7 +763,7 @@ func (r *trustCenterResolver) Organization(ctx context.Context, obj *types.Trust // Accesses is the resolver for the accesses field. func (r *trustCenterResolver) Accesses(ctx context.Context, obj *types.TrustCenter, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.OrderBy[coredata.TrustCenterAccessOrderField]) (*types.TrustCenterAccessConnection, error) { - scope, err := r.authorize(ctx, obj.ID, complianceportal.ActionCompliancePortalAccessList) + scope, err := r.authorize(ctx, obj.ID, management.ActionCompliancePortalAccessList) if err != nil { return nil, err } @@ -794,7 +793,7 @@ func (r *trustCenterResolver) Accesses(ctx context.Context, obj *types.TrustCent // References is the resolver for the references field. func (r *trustCenterResolver) References(ctx context.Context, obj *types.TrustCenter, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.OrderBy[coredata.TrustCenterReferenceOrderField]) (*types.TrustCenterReferenceConnection, error) { - scope, err := r.authorize(ctx, obj.ID, complianceportal.ActionCompliancePortalReferenceList) + scope, err := r.authorize(ctx, obj.ID, management.ActionCompliancePortalReferenceList) if err != nil { return nil, err } @@ -824,7 +823,7 @@ func (r *trustCenterResolver) References(ctx context.Context, obj *types.TrustCe // ComplianceFrameworks is the resolver for the complianceFrameworks field. func (r *trustCenterResolver) ComplianceFrameworks(ctx context.Context, obj *types.TrustCenter, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.OrderBy[coredata.ComplianceFrameworkOrderField]) (*types.ComplianceFrameworkConnection, error) { - scope, err := r.authorize(ctx, obj.ID, complianceportal.ActionComplianceFrameworkList) + scope, err := r.authorize(ctx, obj.ID, management.ActionComplianceFrameworkList) if err != nil { return nil, err } @@ -854,7 +853,7 @@ func (r *trustCenterResolver) ComplianceFrameworks(ctx context.Context, obj *typ // CustomLinks is the resolver for the customLinks field. func (r *trustCenterResolver) CustomLinks(ctx context.Context, obj *types.TrustCenter, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.OrderBy[coredata.ComplianceCustomLinkOrderField]) (*types.ComplianceCustomLinkConnection, error) { - scope, err := r.authorize(ctx, obj.ID, complianceportal.ActionComplianceCustomLinkList) + scope, err := r.authorize(ctx, obj.ID, management.ActionComplianceCustomLinkList) if err != nil { return nil, err } @@ -884,7 +883,7 @@ func (r *trustCenterResolver) CustomLinks(ctx context.Context, obj *types.TrustC // MailingList is the resolver for the mailingList field. func (r *trustCenterResolver) MailingList(ctx context.Context, obj *types.TrustCenter) (*types.MailingList, error) { - scope, err := r.authorize(ctx, obj.ID, complianceportal.ActionMailingListSubscriberList) + scope, err := r.authorize(ctx, obj.ID, management.ActionMailingListSubscriberList) if err != nil { return nil, err } @@ -908,7 +907,7 @@ func (r *trustCenterResolver) MailingList(ctx context.Context, obj *types.TrustC // DefaultDomain is the resolver for the defaultDomain field. func (r *trustCenterResolver) DefaultDomain(ctx context.Context, obj *types.TrustCenter) (*types.CustomDomain, error) { - scope, err := r.authorize(ctx, obj.ID, complianceportal.ActionCustomDomainGet) + scope, err := r.authorize(ctx, obj.ID, management.ActionCustomDomainGet) if err != nil { return nil, err } @@ -928,7 +927,7 @@ func (r *trustCenterResolver) DefaultDomain(ctx context.Context, obj *types.Trus // CustomDomain is the resolver for the customDomain field. func (r *trustCenterResolver) CustomDomain(ctx context.Context, obj *types.TrustCenter) (*types.CustomDomain, error) { - scope, err := r.authorize(ctx, obj.ID, complianceportal.ActionCustomDomainGet) + scope, err := r.authorize(ctx, obj.ID, management.ActionCustomDomainGet) if err != nil { return nil, err } @@ -948,7 +947,7 @@ func (r *trustCenterResolver) CustomDomain(ctx context.Context, obj *types.Trust // PublicURL is the resolver for the publicUrl field. func (r *trustCenterResolver) PublicURL(ctx context.Context, obj *types.TrustCenter) (string, error) { - scope, err := r.authorize(ctx, obj.ID, complianceportal.ActionCompliancePortalGet) + scope, err := r.authorize(ctx, obj.ID, management.ActionCompliancePortalGet) if err != nil { return "", err } @@ -969,7 +968,7 @@ func (r *trustCenterResolver) Permission(ctx context.Context, obj *types.TrustCe // NdaSignature is the resolver for the ndaSignature field. func (r *trustCenterAccessResolver) NdaSignature(ctx context.Context, obj *types.TrustCenterAccess) (*types.ElectronicSignature, error) { - scope, err := r.authorize(ctx, obj.ID, complianceportal.ActionCompliancePortalAccessGet) + scope, err := r.authorize(ctx, obj.ID, management.ActionCompliancePortalAccessGet) if err != nil { return nil, err } @@ -993,7 +992,7 @@ func (r *trustCenterAccessResolver) NdaSignature(ctx context.Context, obj *types // PendingRequestCount is the resolver for the pendingRequestCount field. func (r *trustCenterAccessResolver) PendingRequestCount(ctx context.Context, obj *types.TrustCenterAccess) (int, error) { - scope, err := r.authorize(ctx, obj.ID, complianceportal.ActionCompliancePortalAccessGet) + scope, err := r.authorize(ctx, obj.ID, management.ActionCompliancePortalAccessGet) if err != nil { return 0, err } @@ -1009,7 +1008,7 @@ func (r *trustCenterAccessResolver) PendingRequestCount(ctx context.Context, obj // ActiveCount is the resolver for the activeCount field. func (r *trustCenterAccessResolver) ActiveCount(ctx context.Context, obj *types.TrustCenterAccess) (int, error) { - scope, err := r.authorize(ctx, obj.ID, complianceportal.ActionCompliancePortalAccessGet) + scope, err := r.authorize(ctx, obj.ID, management.ActionCompliancePortalAccessGet) if err != nil { return 0, err } @@ -1045,7 +1044,7 @@ func (r *trustCenterAccessResolver) Profile(ctx context.Context, obj *types.Trus // AvailableDocumentAccesses is the resolver for the availableDocumentAccesses field. func (r *trustCenterAccessResolver) AvailableDocumentAccesses(ctx context.Context, obj *types.TrustCenterAccess, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.OrderBy[coredata.TrustCenterDocumentAccessOrderField]) (*types.TrustCenterDocumentAccessConnection, error) { - scope, err := r.authorize(ctx, obj.ID, complianceportal.ActionCompliancePortalAccessGet) + scope, err := r.authorize(ctx, obj.ID, management.ActionCompliancePortalAccessGet) if err != nil { return nil, err } @@ -1156,7 +1155,7 @@ func (r *trustCenterDocumentAccessResolver) Audit(ctx context.Context, obj *type // TrustCenterFile is the resolver for the trustCenterFile field. func (r *trustCenterDocumentAccessResolver) TrustCenterFile(ctx context.Context, obj *types.TrustCenterDocumentAccess) (*types.TrustCenterFile, error) { - scope, err := r.authorize(ctx, obj.TrustCenterAccessID, complianceportal.ActionCompliancePortalFileGet) + scope, err := r.authorize(ctx, obj.TrustCenterAccessID, management.ActionCompliancePortalFileGet) if err != nil { return nil, err } @@ -1176,7 +1175,7 @@ func (r *trustCenterDocumentAccessResolver) TrustCenterFile(ctx context.Context, // TotalCount is the resolver for the totalCount field. func (r *trustCenterDocumentAccessConnectionResolver) TotalCount(ctx context.Context, obj *types.TrustCenterDocumentAccessConnection) (int, error) { - scope, err := r.authorize(ctx, obj.ParentID, complianceportal.ActionCompliancePortalDocumentAccessList) + scope, err := r.authorize(ctx, obj.ParentID, management.ActionCompliancePortalDocumentAccessList) if err != nil { return 0, err } @@ -1192,7 +1191,7 @@ func (r *trustCenterDocumentAccessConnectionResolver) TotalCount(ctx context.Con // File is the resolver for the file field. func (r *trustCenterFileResolver) File(ctx context.Context, obj *types.TrustCenterFile) (*types.File, error) { - if _, err := r.authorize(ctx, obj.ID, complianceportal.ActionCompliancePortalFileGetFileUrl); err != nil { + if _, err := r.authorize(ctx, obj.ID, management.ActionCompliancePortalFileGetFileUrl); err != nil { return nil, err } @@ -1243,7 +1242,7 @@ func (r *trustCenterFileResolver) Permission(ctx context.Context, obj *types.Tru // TotalCount is the resolver for the totalCount field. func (r *trustCenterFileConnectionResolver) TotalCount(ctx context.Context, obj *types.TrustCenterFileConnection) (int, error) { - scope, err := r.authorize(ctx, obj.ParentID, complianceportal.ActionCompliancePortalFileList) + scope, err := r.authorize(ctx, obj.ParentID, management.ActionCompliancePortalFileList) if err != nil { return 0, err } @@ -1259,7 +1258,7 @@ func (r *trustCenterFileConnectionResolver) TotalCount(ctx context.Context, obj // Logo is the resolver for the logo field. func (r *trustCenterReferenceResolver) Logo(ctx context.Context, obj *types.TrustCenterReference) (*types.File, error) { - if _, err := r.authorize(ctx, obj.ID, complianceportal.ActionCompliancePortalReferenceGetLogoUrl); err != nil { + if _, err := r.authorize(ctx, obj.ID, management.ActionCompliancePortalReferenceGetLogoUrl); err != nil { return nil, err } @@ -1273,7 +1272,7 @@ func (r *trustCenterReferenceResolver) Permission(ctx context.Context, obj *type // TotalCount is the resolver for the totalCount field. func (r *trustCenterReferenceConnectionResolver) TotalCount(ctx context.Context, obj *types.TrustCenterReferenceConnection) (int, error) { - scope, err := r.authorize(ctx, obj.ParentID, complianceportal.ActionCompliancePortalReferenceList) + scope, err := r.authorize(ctx, obj.ParentID, management.ActionCompliancePortalReferenceList) if err != nil { return 0, err } diff --git a/pkg/server/api/mcp/v1/resolver.go b/pkg/server/api/mcp/v1/resolver.go index 34fe074e1..de4341d85 100644 --- a/pkg/server/api/mcp/v1/resolver.go +++ b/pkg/server/api/mcp/v1/resolver.go @@ -31,6 +31,7 @@ import ( "go.gearno.de/kit/log" "go.probo.inc/probo/pkg/accessreview" "go.probo.inc/probo/pkg/baseurl" + "go.probo.inc/probo/pkg/certmanager" "go.probo.inc/probo/pkg/complianceportal/management" "go.probo.inc/probo/pkg/cookiebanner" "go.probo.inc/probo/pkg/coredata" @@ -49,6 +50,7 @@ import ( type Resolver struct { proboSvc *probo.Service management *management.Service + certManager *certmanager.Service resourceAlias *resourcealias.Service thirdPartySvc *thirdparty.Service iamSvc *iam.Service diff --git a/pkg/server/api/mcp/v1/schema.resolvers.go b/pkg/server/api/mcp/v1/schema.resolvers.go index b6b6617d4..03a62e38b 100644 --- a/pkg/server/api/mcp/v1/schema.resolvers.go +++ b/pkg/server/api/mcp/v1/schema.resolvers.go @@ -14,7 +14,6 @@ import ( "github.com/modelcontextprotocol/go-sdk/mcp" "go.gearno.de/kit/log" "go.probo.inc/probo/pkg/accessreview" - "go.probo.inc/probo/pkg/complianceportal" "go.probo.inc/probo/pkg/complianceportal/management" "go.probo.inc/probo/pkg/cookiebanner" "go.probo.inc/probo/pkg/coredata" @@ -4884,7 +4883,7 @@ func (r *Resolver) DeleteRightsRequestTool(ctx context.Context, req *mcp.CallToo // GetTrustCenterTool handles the getTrustCenter tool // Get the trust center for an organization func (r *Resolver) GetTrustCenterTool(ctx context.Context, req *mcp.CallToolRequest, input *types.GetTrustCenterInput) (*mcp.CallToolResult, types.GetTrustCenterOutput, error) { - scope, err := r.Authorize(ctx, input.OrganizationID, complianceportal.ActionCompliancePortalGet) + scope, err := r.Authorize(ctx, input.OrganizationID, management.ActionCompliancePortalGet) if err != nil { return nil, types.GetTrustCenterOutput{}, err } @@ -4931,7 +4930,7 @@ func (r *Resolver) GetTrustCenterTool(ctx context.Context, req *mcp.CallToolRequ // UpdateTrustCenterTool handles the updateTrustCenter tool // Update the trust center settings func (r *Resolver) UpdateTrustCenterTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UpdateTrustCenterInput) (*mcp.CallToolResult, types.UpdateTrustCenterOutput, error) { - scope, err := r.Authorize(ctx, input.TrustCenterID, complianceportal.ActionCompliancePortalUpdate) + scope, err := r.Authorize(ctx, input.TrustCenterID, management.ActionCompliancePortalUpdate) if err != nil { return nil, types.UpdateTrustCenterOutput{}, err } @@ -4969,7 +4968,7 @@ func (r *Resolver) UpdateTrustCenterTool(ctx context.Context, req *mcp.CallToolR // ListTrustCenterReferencesTool handles the listTrustCenterReferences tool // List all references for a trust center func (r *Resolver) ListTrustCenterReferencesTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListTrustCenterReferencesInput) (*mcp.CallToolResult, types.ListTrustCenterReferencesOutput, error) { - scope, err := r.Authorize(ctx, input.TrustCenterID, complianceportal.ActionCompliancePortalReferenceList) + scope, err := r.Authorize(ctx, input.TrustCenterID, management.ActionCompliancePortalReferenceList) if err != nil { return nil, types.ListTrustCenterReferencesOutput{}, err } @@ -5015,7 +5014,7 @@ func (r *Resolver) ListTrustCenterReferencesTool(ctx context.Context, req *mcp.C // AddTrustCenterReferenceTool handles the addTrustCenterReference tool // Add a new reference to the trust center func (r *Resolver) AddTrustCenterReferenceTool(ctx context.Context, req *mcp.CallToolRequest, input *types.AddTrustCenterReferenceInput) (*mcp.CallToolResult, types.AddTrustCenterReferenceOutput, error) { - scope, err := r.Authorize(ctx, input.TrustCenterID, complianceportal.ActionCompliancePortalReferenceCreate) + scope, err := r.Authorize(ctx, input.TrustCenterID, management.ActionCompliancePortalReferenceCreate) if err != nil { return nil, types.AddTrustCenterReferenceOutput{}, err } @@ -5046,7 +5045,7 @@ func (r *Resolver) AddTrustCenterReferenceTool(ctx context.Context, req *mcp.Cal // UpdateTrustCenterReferenceTool handles the updateTrustCenterReference tool // Update a trust center reference func (r *Resolver) UpdateTrustCenterReferenceTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UpdateTrustCenterReferenceInput) (*mcp.CallToolResult, types.UpdateTrustCenterReferenceOutput, error) { - scope, err := r.Authorize(ctx, input.ID, complianceportal.ActionCompliancePortalReferenceUpdate) + scope, err := r.Authorize(ctx, input.ID, management.ActionCompliancePortalReferenceUpdate) if err != nil { return nil, types.UpdateTrustCenterReferenceOutput{}, err } @@ -5081,7 +5080,7 @@ func (r *Resolver) UpdateTrustCenterReferenceTool(ctx context.Context, req *mcp. // DeleteTrustCenterReferenceTool handles the deleteTrustCenterReference tool // Delete a trust center reference func (r *Resolver) DeleteTrustCenterReferenceTool(ctx context.Context, req *mcp.CallToolRequest, input *types.DeleteTrustCenterReferenceInput) (*mcp.CallToolResult, types.DeleteTrustCenterReferenceOutput, error) { - scope, err := r.Authorize(ctx, input.ID, complianceportal.ActionCompliancePortalReferenceDelete) + scope, err := r.Authorize(ctx, input.ID, management.ActionCompliancePortalReferenceDelete) if err != nil { return nil, types.DeleteTrustCenterReferenceOutput{}, err } @@ -5099,7 +5098,7 @@ func (r *Resolver) DeleteTrustCenterReferenceTool(ctx context.Context, req *mcp. // ListTrustCenterFilesTool handles the listTrustCenterFiles tool // List all files for the trust center func (r *Resolver) ListTrustCenterFilesTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListTrustCenterFilesInput) (*mcp.CallToolResult, types.ListTrustCenterFilesOutput, error) { - scope, err := r.Authorize(ctx, input.OrganizationID, complianceportal.ActionCompliancePortalFileList) + scope, err := r.Authorize(ctx, input.OrganizationID, management.ActionCompliancePortalFileList) if err != nil { return nil, types.ListTrustCenterFilesOutput{}, err } @@ -5142,7 +5141,7 @@ func (r *Resolver) ListTrustCenterFilesTool(ctx context.Context, req *mcp.CallTo // DeleteTrustCenterFileTool handles the deleteTrustCenterFile tool // Delete a trust center file func (r *Resolver) DeleteTrustCenterFileTool(ctx context.Context, req *mcp.CallToolRequest, input *types.DeleteTrustCenterFileInput) (*mcp.CallToolResult, types.DeleteTrustCenterFileOutput, error) { - scope, err := r.Authorize(ctx, input.ID, complianceportal.ActionCompliancePortalFileDelete) + scope, err := r.Authorize(ctx, input.ID, management.ActionCompliancePortalFileDelete) if err != nil { return nil, types.DeleteTrustCenterFileOutput{}, err } @@ -5160,7 +5159,7 @@ func (r *Resolver) DeleteTrustCenterFileTool(ctx context.Context, req *mcp.CallT // ListComplianceCustomLinksTool handles the listComplianceCustomLinks tool // List all custom links for a trust center func (r *Resolver) ListComplianceCustomLinksTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListComplianceCustomLinksInput) (*mcp.CallToolResult, types.ListComplianceCustomLinksOutput, error) { - scope, err := r.Authorize(ctx, input.TrustCenterID, complianceportal.ActionComplianceCustomLinkList) + scope, err := r.Authorize(ctx, input.TrustCenterID, management.ActionComplianceCustomLinkList) if err != nil { return nil, types.ListComplianceCustomLinksOutput{}, err } @@ -5192,7 +5191,7 @@ func (r *Resolver) ListComplianceCustomLinksTool(ctx context.Context, req *mcp.C // AddComplianceCustomLinkTool handles the addComplianceCustomLink tool // Add a new custom link to the trust center func (r *Resolver) AddComplianceCustomLinkTool(ctx context.Context, req *mcp.CallToolRequest, input *types.AddComplianceCustomLinkInput) (*mcp.CallToolResult, types.AddComplianceCustomLinkOutput, error) { - scope, err := r.Authorize(ctx, input.TrustCenterID, complianceportal.ActionComplianceCustomLinkCreate) + scope, err := r.Authorize(ctx, input.TrustCenterID, management.ActionComplianceCustomLinkCreate) if err != nil { return nil, types.AddComplianceCustomLinkOutput{}, err } @@ -5217,7 +5216,7 @@ func (r *Resolver) AddComplianceCustomLinkTool(ctx context.Context, req *mcp.Cal // UpdateComplianceCustomLinkTool handles the updateComplianceCustomLink tool // Update a compliance custom link func (r *Resolver) UpdateComplianceCustomLinkTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UpdateComplianceCustomLinkInput) (*mcp.CallToolResult, types.UpdateComplianceCustomLinkOutput, error) { - scope, err := r.Authorize(ctx, input.ID, complianceportal.ActionComplianceCustomLinkUpdate) + scope, err := r.Authorize(ctx, input.ID, management.ActionComplianceCustomLinkUpdate) if err != nil { return nil, types.UpdateComplianceCustomLinkOutput{}, err } @@ -5251,7 +5250,7 @@ func (r *Resolver) UpdateComplianceCustomLinkTool(ctx context.Context, req *mcp. // DeleteComplianceCustomLinkTool handles the deleteComplianceCustomLink tool // Delete a compliance custom link func (r *Resolver) DeleteComplianceCustomLinkTool(ctx context.Context, req *mcp.CallToolRequest, input *types.DeleteComplianceCustomLinkInput) (*mcp.CallToolResult, types.DeleteComplianceCustomLinkOutput, error) { - scope, err := r.Authorize(ctx, input.ID, complianceportal.ActionComplianceCustomLinkDelete) + scope, err := r.Authorize(ctx, input.ID, management.ActionComplianceCustomLinkDelete) if err != nil { return nil, types.DeleteComplianceCustomLinkOutput{}, err } @@ -5274,7 +5273,7 @@ func (r *Resolver) DeleteComplianceCustomLinkTool(ctx context.Context, req *mcp. // CreateCustomDomainTool handles the createCustomDomain tool // Create a custom domain for a compliance page func (r *Resolver) CreateCustomDomainTool(ctx context.Context, req *mcp.CallToolRequest, input *types.CreateCustomDomainInput) (*mcp.CallToolResult, types.CreateCustomDomainOutput, error) { - scope, err := r.Authorize(ctx, input.TrustCenterID, complianceportal.ActionCustomDomainCreate) + scope, err := r.Authorize(ctx, input.TrustCenterID, management.ActionCustomDomainCreate) if err != nil { return nil, types.CreateCustomDomainOutput{}, err } @@ -5288,9 +5287,12 @@ func (r *Resolver) CreateCustomDomainTool(ctx context.Context, req *mcp.CallTool return nil, types.CreateCustomDomainOutput{}, fmt.Errorf("cannot create custom domain: %w", err) } - cert, err := r.management.GetCertificate(ctx, scope, domain) - if err != nil { - return nil, types.CreateCustomDomainOutput{}, fmt.Errorf("cannot load certificate: %w", err) + var cert *coredata.Certificate + if domain.CertificateID != nil { + cert, err = r.certManager.Get(ctx, scope, *domain.CertificateID) + if err != nil { + return nil, types.CreateCustomDomainOutput{}, fmt.Errorf("cannot load certificate: %w", err) + } } return nil, types.CreateCustomDomainOutput{CustomDomain: types.NewCustomDomain(domain, cert)}, nil @@ -5299,7 +5301,7 @@ func (r *Resolver) CreateCustomDomainTool(ctx context.Context, req *mcp.CallTool // DeleteCustomDomainTool handles the deleteCustomDomain tool // Delete the custom domain of a compliance page func (r *Resolver) DeleteCustomDomainTool(ctx context.Context, req *mcp.CallToolRequest, input *types.DeleteCustomDomainInput) (*mcp.CallToolResult, types.DeleteCustomDomainOutput, error) { - scope, err := r.Authorize(ctx, input.TrustCenterID, complianceportal.ActionCustomDomainDelete) + scope, err := r.Authorize(ctx, input.TrustCenterID, management.ActionCustomDomainDelete) if err != nil { return nil, types.DeleteCustomDomainOutput{}, err } @@ -5313,9 +5315,12 @@ func (r *Resolver) DeleteCustomDomainTool(ctx context.Context, req *mcp.CallTool return nil, types.DeleteCustomDomainOutput{}, fmt.Errorf("compliance page has no custom domain") } - cert, err := r.management.GetCertificate(ctx, scope, domain) - if err != nil { - return nil, types.DeleteCustomDomainOutput{}, fmt.Errorf("cannot load certificate: %w", err) + var cert *coredata.Certificate + if domain.CertificateID != nil { + cert, err = r.certManager.Get(ctx, scope, *domain.CertificateID) + if err != nil { + return nil, types.DeleteCustomDomainOutput{}, fmt.Errorf("cannot load certificate: %w", err) + } } deletedDomain := types.NewCustomDomain(domain, cert) diff --git a/pkg/server/api/mcp/v1/v1_handler.go b/pkg/server/api/mcp/v1/v1_handler.go index 5253d96eb..84987833a 100644 --- a/pkg/server/api/mcp/v1/v1_handler.go +++ b/pkg/server/api/mcp/v1/v1_handler.go @@ -29,6 +29,7 @@ import ( mcpgenmcp "go.probo.inc/mcpgen/mcp" "go.probo.inc/probo/pkg/accessreview" "go.probo.inc/probo/pkg/baseurl" + "go.probo.inc/probo/pkg/certmanager" "go.probo.inc/probo/pkg/complianceportal/management" "go.probo.inc/probo/pkg/cookiebanner" "go.probo.inc/probo/pkg/filemanager" @@ -46,6 +47,7 @@ func NewMux( logger *log.Logger, proboSvc *probo.Service, managementSvc *management.Service, + certManagerSvc *certmanager.Service, resourceAliasSvc *resourcealias.Service, thirdPartySvc *thirdparty.Service, iamSvc *iam.Service, @@ -63,6 +65,7 @@ func NewMux( resolver := &Resolver{ proboSvc: proboSvc, management: managementSvc, + certManager: certManagerSvc, resourceAlias: resourceAliasSvc, thirdPartySvc: thirdPartySvc, iamSvc: iamSvc, diff --git a/pkg/server/server.go b/pkg/server/server.go index 42131a9a7..39505f7e9 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -29,6 +29,7 @@ import ( "go.probo.inc/probo/pkg/accessreview" "go.probo.inc/probo/pkg/agentrun" "go.probo.inc/probo/pkg/baseurl" + "go.probo.inc/probo/pkg/certmanager" "go.probo.inc/probo/pkg/complianceportal/management" "go.probo.inc/probo/pkg/complianceportal/visitor" "go.probo.inc/probo/pkg/connector" @@ -64,6 +65,7 @@ type Config struct { Trust *visitor.Service ESign *esign.Service Management *management.Service + CertManager *certmanager.Service AccessReview *accessreview.Service AgentRun *agentrun.Service Slack *slack.Service @@ -105,6 +107,7 @@ func NewServer(cfg Config) (*Server, error) { Trust: cfg.Trust, ESign: cfg.ESign, Management: cfg.Management, + CertManager: cfg.CertManager, AccessReview: cfg.AccessReview, AgentRun: cfg.AgentRun, Slack: cfg.Slack, diff --git a/pkg/slug/slug.go b/pkg/slug/slug.go index 5f137c79a..6fc09fd7a 100644 --- a/pkg/slug/slug.go +++ b/pkg/slug/slug.go @@ -23,6 +23,8 @@ package slug import ( "regexp" "strings" + + "go.probo.inc/probo/pkg/crypto/rand" ) var ( @@ -42,3 +44,14 @@ func Make(s string) string { return s } + +func MakeWithEntropy(s string) string { + base := Make(s) + suffix := rand.MustHexString(4) + + if base == "" { + return suffix + } + + return base + "-" + suffix +} diff --git a/pkg/slug/slug_test.go b/pkg/slug/slug_test.go index 1207df68f..dbe9ca95d 100644 --- a/pkg/slug/slug_test.go +++ b/pkg/slug/slug_test.go @@ -22,30 +22,73 @@ package slug import ( "testing" + + "github.com/stretchr/testify/assert" ) func TestMake(t *testing.T) { + t.Parallel() + tests := []struct { + name string input string expected string }{ - {"Hello World", "hello-world"}, - {"This is a test", "this-is-a-test"}, - {"Special characters: !@#$%^&*()", "special-characters"}, - {"Multiple---Hyphens", "multiple-hyphens"}, - {"-Trim-Hyphens-", "trim-hyphens"}, - {"123 Numbers", "123-numbers"}, - {" Spaces ", "spaces"}, - {"", ""}, - {"UPPERCASE", "uppercase"}, - {"under_score", "under-score"}, - {"dots.and.more.dots", "dotsandmoredots"}, + {name: "hello world", input: "Hello World", expected: "hello-world"}, + {name: "this is a test", input: "This is a test", expected: "this-is-a-test"}, + {name: "special characters", input: "Special characters: !@#$%^&*()", expected: "special-characters"}, + {name: "multiple hyphens", input: "Multiple---Hyphens", expected: "multiple-hyphens"}, + {name: "trim hyphens", input: "-Trim-Hyphens-", expected: "trim-hyphens"}, + {name: "numbers", input: "123 Numbers", expected: "123-numbers"}, + {name: "spaces", input: " Spaces ", expected: "spaces"}, + {name: "empty", input: "", expected: ""}, + {name: "uppercase", input: "UPPERCASE", expected: "uppercase"}, + {name: "underscore", input: "under_score", expected: "under-score"}, + {name: "dots", input: "dots.and.more.dots", expected: "dotsandmoredots"}, } - for _, test := range tests { - slug := Make(test.input) - if slug != test.expected { - t.Errorf("Slug(%q) = %q; expected %q", test.input, slug, test.expected) - } + for _, tt := range tests { + t.Run( + tt.name, + func(t *testing.T) { + t.Parallel() + + assert.Equal(t, tt.expected, Make(tt.input)) + }, + ) } } + +func TestMakeWithEntropy(t *testing.T) { + t.Parallel() + + t.Run( + "empty input returns hex only", + func(t *testing.T) { + t.Parallel() + + assert.Regexp(t, `^[0-9a-f]{8}$`, MakeWithEntropy("")) + }, + ) + + t.Run( + "non-empty input returns base with hex suffix", + func(t *testing.T) { + t.Parallel() + + got := MakeWithEntropy("Hello World") + assert.Regexp(t, `^hello-world-[0-9a-f]{8}$`, got) + }, + ) + + t.Run( + "successive calls differ", + func(t *testing.T) { + t.Parallel() + + first := MakeWithEntropy("Acme Corp") + second := MakeWithEntropy("Acme Corp") + assert.NotEqual(t, first, second, "MakeWithEntropy should produce distinct slugs") + }, + ) +}