From dbf022f772911fd38667c0ba75944e4c262df8cb Mon Sep 17 00:00:00 2001 From: Bryan Frimin Date: Mon, 22 Jun 2026 08:39:33 +0200 Subject: [PATCH] Add TrustCenterAlias application service Exposes Create, Remove, GetByResourceID, and LoadByResourceIDs. Registers ActionTrustCenterAliasSet and ActionTrustCenterAliasRemove under the trust center write OAuth2 scope. Signed-off-by: Bryan Frimin --- pkg/probo/actions.go | 4 + pkg/probo/oauth2_scopes.go | 2 + pkg/probo/service.go | 2 + pkg/probo/trust_center_alias_service.go | 211 ++++++++++++++++++++++++ 4 files changed, 219 insertions(+) create mode 100644 pkg/probo/trust_center_alias_service.go diff --git a/pkg/probo/actions.go b/pkg/probo/actions.go index 7079b5953..c5615c8bf 100644 --- a/pkg/probo/actions.go +++ b/pkg/probo/actions.go @@ -75,6 +75,10 @@ const ( ActionComplianceExternalURLUpdate = "core:compliance-external-url:update" ActionComplianceExternalURLDelete = "core:compliance-external-url:delete" + // TrustCenterAlias actions + ActionTrustCenterAliasSet = "core:trust-center-alias:set" + ActionTrustCenterAliasRemove = "core:trust-center-alias:remove" + // TrustCenterFile actions ActionTrustCenterFileGet = "core:trust-center-file:get" ActionTrustCenterFileList = "core:trust-center-file:list" diff --git a/pkg/probo/oauth2_scopes.go b/pkg/probo/oauth2_scopes.go index ea32a783d..44ba7c6b2 100644 --- a/pkg/probo/oauth2_scopes.go +++ b/pkg/probo/oauth2_scopes.go @@ -149,6 +149,8 @@ var OAuth2ScopeMappings = map[coredata.OAuth2Scope][]string{ ActionComplianceExternalURLDelete, ActionCustomDomainCreate, ActionCustomDomainDelete, + ActionTrustCenterAliasSet, + ActionTrustCenterAliasRemove, }, ScopeV1ConnectorRead: { ActionConnectorList, diff --git a/pkg/probo/service.go b/pkg/probo/service.go index 8a82f3a7d..fe4a06cc1 100644 --- a/pkg/probo/service.go +++ b/pkg/probo/service.go @@ -107,6 +107,7 @@ type ( TrustCenters *TrustCenterService TrustCenterAccesses *TrustCenterAccessService TrustCenterReferences *TrustCenterReferenceService + TrustCenterAliases *TrustCenterAliasService TrustCenterFiles *TrustCenterFileService ComplianceFrameworks *ComplianceFrameworkService ComplianceExternalURLs *ComplianceExternalURLService @@ -228,6 +229,7 @@ func NewService( svc.TrustCenters = &TrustCenterService{svc: svc} svc.TrustCenterAccesses = &TrustCenterAccessService{svc: svc} svc.TrustCenterReferences = &TrustCenterReferenceService{svc: svc} + svc.TrustCenterAliases = &TrustCenterAliasService{svc: svc} svc.ComplianceFrameworks = &ComplianceFrameworkService{svc: svc} svc.ComplianceExternalURLs = &ComplianceExternalURLService{svc: svc} svc.TrustCenterFiles = &TrustCenterFileService{ diff --git a/pkg/probo/trust_center_alias_service.go b/pkg/probo/trust_center_alias_service.go new file mode 100644 index 000000000..f8e72d633 --- /dev/null +++ b/pkg/probo/trust_center_alias_service.go @@ -0,0 +1,211 @@ +// 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 probo + +import ( + "context" + "errors" + "fmt" + + "go.gearno.de/kit/pg" + "go.probo.inc/probo/pkg/coredata" + "go.probo.inc/probo/pkg/gid" + "go.probo.inc/probo/pkg/validator" +) + +type ( + TrustCenterAliasService struct { + svc *Service + } + + CreateTrustCenterAliasRequest struct { + ResourceID gid.GID + Alias string + } + + ErrTrustCenterAliasResourceInvalid struct { + ResourceID gid.GID + } + + ErrTrustCenterAliasAuditReportMissing struct { + AuditID gid.GID + } +) + +func (e ErrTrustCenterAliasResourceInvalid) Error() string { + return fmt.Sprintf("resource %q cannot have a trust center alias", e.ResourceID) +} + +func (e ErrTrustCenterAliasAuditReportMissing) Error() string { + return fmt.Sprintf("audit %q has no report file", e.AuditID) +} + +func (req *CreateTrustCenterAliasRequest) Validate() error { + v := validator.New() + + v.Check(req.ResourceID, "resource_id", validator.Required(), validator.GID()) + v.Check(req.Alias, "alias", validator.Required(), validator.Slug(NameMaxLength)) + + return v.Error() +} + +func (s TrustCenterAliasService) ResolveAlias( + ctx context.Context, + scope coredata.Scoper, + organizationID gid.GID, + alias string, +) (gid.GID, error) { + record := &coredata.TrustCenterAlias{} + + err := s.svc.pg.WithConn( + ctx, + func(ctx context.Context, conn pg.Querier) error { + if err := record.LoadByAlias(ctx, conn, scope, organizationID, alias); err != nil { + return fmt.Errorf("cannot load trust center alias: %w", err) + } + + return nil + }, + ) + if err != nil { + return gid.Nil, err + } + + return record.ResourceID, nil +} + +func (s TrustCenterAliasService) Create( + ctx context.Context, + scope coredata.Scoper, + req CreateTrustCenterAliasRequest, +) (*coredata.TrustCenterAlias, error) { + if err := req.Validate(); err != nil { + return nil, err + } + + aliasResourceID, err := s.aliasResourceID(ctx, scope, req.ResourceID) + if err != nil { + return nil, err + } + + alias := &coredata.TrustCenterAlias{} + + err = s.svc.pg.WithConn( + ctx, + func(ctx context.Context, conn pg.Querier) error { + if err := alias.Upsert(ctx, conn, scope, aliasResourceID, req.Alias); err != nil { + return fmt.Errorf("cannot create trust center alias: %w", err) + } + + return nil + }, + ) + if err != nil { + return nil, err + } + + return alias, nil +} + +func (s TrustCenterAliasService) Remove( + ctx context.Context, + scope coredata.Scoper, + resourceID gid.GID, +) (gid.GID, error) { + aliasResourceID, err := s.aliasResourceID(ctx, scope, resourceID) + if err != nil { + return gid.Nil, err + } + + err = s.svc.pg.WithConn( + ctx, + func(ctx context.Context, conn pg.Querier) error { + alias := &coredata.TrustCenterAlias{ResourceID: aliasResourceID} + if err := alias.Delete(ctx, conn, scope); err != nil { + return fmt.Errorf("cannot remove trust center alias: %w", err) + } + + return nil + }, + ) + if err != nil { + return gid.Nil, err + } + + return aliasResourceID, nil +} + +func (s TrustCenterAliasService) GetByResourceID( + ctx context.Context, + scope coredata.Scoper, + resourceID gid.GID, +) (*string, error) { + aliasResourceID, err := s.aliasResourceID(ctx, scope, resourceID) + if err != nil { + if errors.Is(err, coredata.ErrResourceNotFound) { + return nil, nil + } + + return nil, err + } + + alias := &coredata.TrustCenterAlias{} + + err = s.svc.pg.WithConn( + ctx, + func(ctx context.Context, conn pg.Querier) error { + if err := alias.LoadByResourceID(ctx, conn, scope, aliasResourceID); err != nil { + return fmt.Errorf("cannot load trust center alias: %w", err) + } + + return nil + }, + ) + if err != nil { + if errors.Is(err, coredata.ErrResourceNotFound) { + return nil, nil + } + + return nil, err + } + + return &alias.Alias, nil +} + +func (s TrustCenterAliasService) aliasResourceID( + ctx context.Context, + scope coredata.Scoper, + resourceID gid.GID, +) (gid.GID, error) { + switch resourceID.EntityType() { + case coredata.DocumentEntityType, coredata.TrustCenterFileEntityType: + return resourceID, nil + + case coredata.AuditEntityType: + audit, err := s.svc.Audits.Get(ctx, scope, resourceID) + if err != nil { + return gid.Nil, err + } + + if audit.ReportFileID == nil { + return gid.Nil, &ErrTrustCenterAliasAuditReportMissing{AuditID: audit.ID} + } + + return *audit.ReportFileID, nil + + default: + return gid.Nil, &ErrTrustCenterAliasResourceInvalid{ResourceID: resourceID} + } +}