Files
probo/pkg/server/api/trust/v1/trust_center_resolvers.go
Ludovic Vielle eccef41767 Adopt File type for trust logos and MCP
Trust GraphQL and MCP still exposed presigned URL strings for
trust-center logos while console and connect already serve stable
File.downloadUrl paths. Phase 1 migrates the seven public logo
fields on trust GraphQL and the trust-center file references on MCP
to the shared File type; trust GraphQL NDA stays on fileUrl for a
follow-up.

Trust resolvers load public files through filemanager and map them
with types.NewFile. The trust app Relay queries and components now
read logo.downloadUrl. MCP specification, resolvers, and helpers
are updated in sync, including NDA on MCP where callers already
have file access.

filemanager is split into focused files and its URL surface is
narrowed to GenerateFileURL(file) for stable app URLs and
GeneratePresignedURL for S3 redirects. GetPublicFile remains the
DB entry point when only a file ID is known.

Add trust and MCP e2e coverage for public logo download URLs.

Signed-off-by: Ludovic Vielle <ludovic@probo.com>
2026-06-11 16:03:45 +02:00

1053 lines
36 KiB
Go

package trust_v1
// This file will be automatically regenerated based on the schema, any resolver
// implementations
// will be copied through when generating and any unknown code will be moved to the end.
// Code generated by github.com/99designs/gqlgen version v0.17.90
import (
"context"
"encoding/base64"
"errors"
"fmt"
"go.gearno.de/kit/log"
"go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/page"
"go.probo.inc/probo/pkg/server/api/authn"
"go.probo.inc/probo/pkg/server/api/compliancepage"
"go.probo.inc/probo/pkg/server/api/trust/v1/schema"
"go.probo.inc/probo/pkg/server/api/trust/v1/types"
"go.probo.inc/probo/pkg/server/gqlutils"
"go.probo.inc/probo/pkg/trust"
)
// Framework is the resolver for the framework field.
func (r *auditResolver) Framework(ctx context.Context, obj *types.Audit) (*types.Framework, error) {
scope := coredata.NewScopeFromObjectID(obj.ID)
trustService := r.trust
audit, err := trustService.Audits.Get(ctx, scope, obj.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot load audit", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
framework, err := trustService.Frameworks.Get(ctx, scope, audit.FrameworkID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot load framework", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewFramework(framework), nil
}
// ReportFile is the resolver for the reportFile field.
func (r *auditResolver) ReportFile(ctx context.Context, obj *types.Audit) (*types.AuditReport, error) {
scope := coredata.NewScopeFromObjectID(obj.ID)
trustService := r.trust
audit, err := trustService.Audits.Get(ctx, scope, obj.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot load audit", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
if audit.ReportFileID == nil {
return nil, nil
}
trustCenter := compliancepage.CompliancePageFromContext(ctx)
file, err := trustService.Reports.Get(ctx, scope, trustCenter.OrganizationID, *audit.ReportFileID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot load report file", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewAuditReport(file), nil
}
// IsUserAuthorized is the resolver for the isUserAuthorized field.
func (r *auditReportResolver) IsUserAuthorized(ctx context.Context, obj *types.AuditReport) (bool, error) {
scope := coredata.NewScopeFromObjectID(obj.ID)
trustService := r.trust
trustCenter := compliancepage.CompliancePageFromContext(ctx)
audit, err := trustService.Audits.GetByReportFileID(ctx, scope, obj.ID)
if err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) {
return false, nil
}
r.logger.ErrorCtx(ctx, "cannot load audit for report file", log.Error(err))
return false, gqlutils.Internal(ctx)
}
if audit.TrustCenterVisibility == coredata.TrustCenterVisibilityPublic {
return true, nil
}
identity := authn.IdentityFromContext(ctx)
if identity == nil {
return false, nil
}
reportAccess, err := trustService.TrustCenterAccesses.GetReportFileAccess(ctx, scope,
trustCenter.ID,
identity.ID,
obj.ID,
)
if err != nil {
if errors.Is(err, trust.ErrMembershipNotFound) ||
errors.Is(err, trust.ErrUserNotFound) ||
errors.Is(err, trust.ErrUserInactive) ||
errors.Is(err, trust.ErrDocumentAccessNotFound) {
return false, nil
}
r.logger.ErrorCtx(ctx, "cannot check report access", log.Error(err))
return false, gqlutils.Internal(ctx)
}
return reportAccess.Status == coredata.TrustCenterDocumentAccessStatusGranted, nil
}
// Access is the resolver for the access field.
func (r *auditReportResolver) Access(ctx context.Context, obj *types.AuditReport) (*types.DocumentAccess, error) {
scope := coredata.NewScopeFromObjectID(obj.ID)
trustService := r.trust
trustCenter := compliancepage.CompliancePageFromContext(ctx)
identity := authn.IdentityFromContext(ctx)
if identity == nil {
return nil, nil
}
access, err := trustService.TrustCenterAccesses.GetReportFileAccess(
ctx, scope,
trustCenter.ID,
identity.ID,
obj.ID,
)
if err != nil {
if errors.Is(err, trust.ErrMembershipNotFound) ||
errors.Is(err, trust.ErrUserNotFound) ||
errors.Is(err, trust.ErrDocumentAccessNotFound) {
return nil, nil
}
if errors.Is(err, trust.ErrUserInactive) {
return nil, gqlutils.Forbidden(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot get audit report access", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DocumentAccess{
ID: access.ID,
Status: access.Status,
}, nil
}
// Framework is the resolver for the framework field on ComplianceFramework.
func (r *complianceFrameworkResolver) Framework(ctx context.Context, obj *types.ComplianceFramework) (*types.Framework, error) {
scope := coredata.NewScopeFromObjectID(obj.ID)
trustService := r.trust
framework, err := trustService.Frameworks.Get(ctx, scope, obj.FrameworkID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot load framework", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewFramework(framework), nil
}
// IsUserAuthorized is the resolver for the isUserAuthorized field.
func (r *documentResolver) IsUserAuthorized(ctx context.Context, obj *types.Document) (bool, error) {
scope := coredata.NewScopeFromObjectID(obj.ID)
trustService := r.trust
trustCenter := compliancepage.CompliancePageFromContext(ctx)
document, err := trustService.Documents.Get(ctx, scope, trustCenter.OrganizationID, obj.ID)
if err != nil {
if errors.Is(err, trust.ErrDocumentNotFound) || errors.Is(err, trust.ErrDocumentNotVisible) || errors.Is(err, coredata.ErrResourceNotFound) {
return false, gqlutils.NotFoundf(ctx, "document %q not found", obj.ID)
}
if _, ok := errors.AsType[*trust.ErrDocumentArchived](err); ok {
return false, gqlutils.NotFoundf(ctx, "document %q not found", obj.ID)
}
r.logger.ErrorCtx(ctx, "cannot load document", log.Error(err))
return false, gqlutils.Internal(ctx)
}
if document.TrustCenterVisibility == coredata.TrustCenterVisibilityPublic {
return true, nil
}
identity := authn.IdentityFromContext(ctx)
if identity == nil {
return false, nil
}
documentAccess, err := trustService.TrustCenterAccesses.GetDocumentAccess(
ctx, scope,
trustCenter.ID,
identity.ID,
obj.ID,
)
if err != nil {
if errors.Is(err, trust.ErrMembershipNotFound) ||
errors.Is(err, trust.ErrUserNotFound) ||
errors.Is(err, trust.ErrUserInactive) ||
errors.Is(err, trust.ErrDocumentAccessNotFound) {
return false, nil
}
r.logger.ErrorCtx(ctx, "cannot check document access", log.Error(err))
return false, gqlutils.Internal(ctx)
}
return documentAccess.Status == coredata.TrustCenterDocumentAccessStatusGranted, nil
}
// Access is the resolver for the access field.
func (r *documentResolver) Access(ctx context.Context, obj *types.Document) (*types.DocumentAccess, error) {
scope := coredata.NewScopeFromObjectID(obj.ID)
trustService := r.trust
trustCenter := compliancepage.CompliancePageFromContext(ctx)
identity := authn.IdentityFromContext(ctx)
if identity == nil {
return nil, nil // User is not authenticated, so no access requested
}
access, err := trustService.TrustCenterAccesses.GetDocumentAccess(
ctx, scope,
trustCenter.ID,
identity.ID,
obj.ID,
)
if err != nil {
if errors.Is(err, trust.ErrMembershipNotFound) ||
errors.Is(err, trust.ErrUserNotFound) ||
errors.Is(err, trust.ErrDocumentAccessNotFound) {
return nil, nil
}
if errors.Is(err, trust.ErrUserInactive) {
return nil, gqlutils.Forbidden(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot get document access", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DocumentAccess{
ID: access.ID,
Status: access.Status,
}, nil
}
// LightLogo is the resolver for the lightLogo field.
func (r *frameworkResolver) LightLogo(ctx context.Context, obj *types.Framework) (*types.File, error) {
scope := coredata.NewScopeFromObjectID(obj.ID)
framework, err := r.trust.Frameworks.Get(ctx, scope, obj.ID)
if err != nil {
return nil, gqlutils.NotFoundf(ctx, "framework %q not found", obj.ID)
}
if framework.LightLogoFileID == nil {
return nil, nil
}
return r.loadPublicFile(ctx, *framework.LightLogoFileID)
}
// DarkLogo is the resolver for the darkLogo field.
func (r *frameworkResolver) DarkLogo(ctx context.Context, obj *types.Framework) (*types.File, error) {
scope := coredata.NewScopeFromObjectID(obj.ID)
framework, err := r.trust.Frameworks.Get(ctx, scope, obj.ID)
if err != nil {
return nil, gqlutils.NotFoundf(ctx, "framework %q not found", obj.ID)
}
if framework.DarkLogoFileID == nil {
return nil, nil
}
return r.loadPublicFile(ctx, *framework.DarkLogoFileID)
}
// RequestAllAccesses is the resolver for the requestAllAccesses field.
func (r *mutationResolver) RequestAllAccesses(ctx context.Context) (*types.RequestAccessesPayload, error) {
trustCenter := compliancepage.CompliancePageFromContext(ctx)
scope := coredata.NewScopeFromObjectID(trustCenter.ID)
trustService := r.trust
identity := authn.IdentityFromContext(ctx)
if identity == nil {
return nil, gqlutils.Unauthenticatedf(ctx, "authentication is required to request access")
}
access, err := trustService.TrustCenterAccesses.Request(
ctx, scope,
&trust.TrustCenterAccessRequest{
TrustCenterID: trustCenter.ID,
IdentityID: identity.ID,
DocumentIDs: nil,
ReportIDs: nil,
},
)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot create trust center access", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.RequestAccessesPayload{
TrustCenterAccess: &types.TrustCenterAccess{
ID: access.ID,
CreatedAt: access.CreatedAt,
UpdatedAt: access.UpdatedAt,
},
}, nil
}
// ExportDocumentPDF is the resolver for the exportDocumentPDF field.
func (r *mutationResolver) ExportDocumentPDF(ctx context.Context, input types.ExportDocumentPDFInput) (*types.ExportDocumentPDFPayload, error) {
scope := coredata.NewScopeFromObjectID(input.DocumentID)
trustService := r.trust
trustCenter := compliancepage.CompliancePageFromContext(ctx)
document, err := trustService.Documents.Get(ctx, scope, trustCenter.OrganizationID, input.DocumentID)
if err != nil {
if errors.Is(err, trust.ErrDocumentNotFound) || errors.Is(err, trust.ErrDocumentNotVisible) || errors.Is(err, coredata.ErrResourceNotFound) {
return nil, gqlutils.NotFoundf(ctx, "document %q not found", input.DocumentID)
}
if _, ok := errors.AsType[*trust.ErrDocumentArchived](err); ok {
return nil, gqlutils.NotFoundf(ctx, "document %q not found", input.DocumentID)
}
r.logger.ErrorCtx(ctx, "cannot load document", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
if document.TrustCenterVisibility == coredata.TrustCenterVisibilityPublic {
pdf, err := trustService.Documents.ExportPDFWithoutWatermark(ctx, scope, input.DocumentID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot export document PDF", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.ExportDocumentPDFPayload{
Data: fmt.Sprintf("data:application/pdf;base64,%s", base64.StdEncoding.EncodeToString(pdf)),
}, nil
}
identity := authn.IdentityFromContext(ctx)
if identity == nil {
return nil, gqlutils.Unauthenticated(ctx, errors.New("unauthenticated"))
}
documentAccess, err := trustService.TrustCenterAccesses.GetDocumentAccess(
ctx, scope,
trustCenter.ID,
identity.ID,
input.DocumentID,
)
if err != nil {
return nil, nil
}
if documentAccess.Status != coredata.TrustCenterDocumentAccessStatusGranted {
return nil, gqlutils.Forbiddenf(ctx, "access denied: no permission to access this document")
}
pdf, err := trustService.Documents.ExportPDF(ctx, scope, input.DocumentID, identity.EmailAddress)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot export document PDF", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.ExportDocumentPDFPayload{
Data: fmt.Sprintf("data:application/pdf;base64,%s", base64.StdEncoding.EncodeToString(pdf)),
}, nil
}
// ExportReportPDF is the resolver for the exportReportPDF field.
func (r *mutationResolver) ExportReportPDF(ctx context.Context, input types.ExportReportPDFInput) (*types.ExportReportPDFPayload, error) {
scope := coredata.NewScopeFromObjectID(input.ReportID)
trustService := r.trust
trustCenter := compliancepage.CompliancePageFromContext(ctx)
audit, err := trustService.Audits.GetByReportFileID(ctx, scope, input.ReportID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot load audit", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
if audit.TrustCenterVisibility == coredata.TrustCenterVisibilityPublic {
pdf, err := trustService.Reports.ExportPDFWithoutWatermark(ctx, scope, input.ReportID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot export report PDF", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.ExportReportPDFPayload{
Data: fmt.Sprintf("data:application/pdf;base64,%s", base64.StdEncoding.EncodeToString(pdf)),
}, nil
}
identity := authn.IdentityFromContext(ctx)
if identity == nil {
return nil, gqlutils.Unauthenticatedf(ctx, "unauthenticated")
}
reportAccess, err := trustService.TrustCenterAccesses.GetReportFileAccess(
ctx, scope,
trustCenter.ID,
identity.ID,
input.ReportID,
)
if err != nil {
return nil, nil
}
if reportAccess.Status != coredata.TrustCenterDocumentAccessStatusGranted {
return nil, gqlutils.Forbiddenf(ctx, "access denied: no permission to access this report")
}
pdf, err := trustService.Reports.ExportPDF(ctx, scope, input.ReportID, identity.EmailAddress)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot export report PDF", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.ExportReportPDFPayload{
Data: fmt.Sprintf("data:application/pdf;base64,%s", base64.StdEncoding.EncodeToString(pdf)),
}, nil
}
// ExportTrustCenterFile is the resolver for the exportTrustCenterFile field.
func (r *mutationResolver) ExportTrustCenterFile(ctx context.Context, input types.ExportTrustCenterFileInput) (*types.ExportTrustCenterFilePayload, error) {
trustCenter := compliancepage.CompliancePageFromContext(ctx)
scope := coredata.NewScopeFromObjectID(trustCenter.ID)
trustService := r.trust
trustCenterFile, err := trustService.TrustCenterFiles.Get(ctx, scope, trustCenter.OrganizationID, input.TrustCenterFileID)
if err != nil {
if errors.Is(err, trust.ErrTrustCenterFileNotFound) || errors.Is(err, trust.ErrTrustCenterFileNotVisible) {
return nil, gqlutils.NotFoundf(ctx, "trust center file %q not found", input.TrustCenterFileID)
}
r.logger.ErrorCtx(ctx, "cannot load trust center file", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
if trustCenterFile.TrustCenterVisibility == coredata.TrustCenterVisibilityPublic {
fileData, mimeType, err := trustService.TrustCenterFiles.ExportFileWithoutWatermark(ctx, scope, input.TrustCenterFileID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot export trust center file", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.ExportTrustCenterFilePayload{
Data: fmt.Sprintf("data:%s;base64,%s", mimeType, base64.StdEncoding.EncodeToString(fileData)),
}, nil
}
identity := authn.IdentityFromContext(ctx)
if identity == nil {
return nil, gqlutils.Unauthenticatedf(ctx, "unauthenticated")
}
fileAccess, err := trustService.TrustCenterAccesses.GetTrustCenterFileAccess(ctx, scope,
trustCenter.ID,
identity.ID,
input.TrustCenterFileID,
)
if err != nil {
return nil, nil
}
if fileAccess.Status != coredata.TrustCenterDocumentAccessStatusGranted {
return nil, gqlutils.Forbiddenf(ctx, "access denied: no permission to access this file")
}
fileData, mimeType, err := trustService.TrustCenterFiles.ExportFile(ctx, scope, input.TrustCenterFileID, identity.EmailAddress)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot export trust center file", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.ExportTrustCenterFilePayload{
Data: fmt.Sprintf("data:%s;base64,%s", mimeType, base64.StdEncoding.EncodeToString(fileData)),
}, nil
}
// RequestDocumentAccess is the resolver for the requestDocumentAccess field.
func (r *mutationResolver) RequestDocumentAccess(ctx context.Context, input types.RequestDocumentAccessInput) (*types.RequestDocumentAccessPayload, error) {
trustCenter := compliancepage.CompliancePageFromContext(ctx)
scope := coredata.NewScopeFromObjectID(trustCenter.ID)
trustService := r.trust
document, err := trustService.Documents.Get(ctx, scope, trustCenter.OrganizationID, input.DocumentID)
if err != nil {
if errors.Is(err, trust.ErrDocumentNotFound) || errors.Is(err, trust.ErrDocumentNotVisible) || errors.Is(err, coredata.ErrResourceNotFound) {
return nil, gqlutils.NotFoundf(ctx, "document %q not found", input.DocumentID)
}
if _, ok := errors.AsType[*trust.ErrDocumentArchived](err); ok {
return nil, gqlutils.NotFoundf(ctx, "document %q not found", input.DocumentID)
}
r.logger.ErrorCtx(ctx, "cannot load document", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
if document.TrustCenterVisibility == coredata.TrustCenterVisibilityPublic {
return nil, gqlutils.Invalidf(
ctx,
"document is publicly available and does not require access request",
)
}
identity := authn.IdentityFromContext(ctx)
if identity == nil {
return nil, gqlutils.Unauthenticatedf(ctx, "authentication is required to request access")
}
if _, err := trustService.TrustCenterAccesses.Request(
ctx, scope,
&trust.TrustCenterAccessRequest{
TrustCenterID: trustCenter.ID,
IdentityID: identity.ID,
DocumentIDs: []gid.GID{input.DocumentID},
ReportIDs: []gid.GID{},
TrustCenterFileIDs: []gid.GID{},
},
); err != nil {
r.logger.ErrorCtx(ctx, "cannot request document access", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.RequestDocumentAccessPayload{
Document: types.NewDocument(document),
}, nil
}
// RequestReportAccess is the resolver for the requestReportAccess field.
func (r *mutationResolver) RequestReportAccess(ctx context.Context, input types.RequestReportAccessInput) (*types.RequestReportAccessPayload, error) {
trustCenter := compliancepage.CompliancePageFromContext(ctx)
scope := coredata.NewScopeFromObjectID(trustCenter.ID)
trustService := r.trust
audit, err := trustService.Audits.GetByReportFileID(ctx, scope, input.ReportID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot load audit", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
if audit.TrustCenterVisibility == coredata.TrustCenterVisibilityPublic {
return nil, gqlutils.Invalidf(
ctx,
"report is publicly available and does not require access request",
)
}
identity := authn.IdentityFromContext(ctx)
if identity == nil {
return nil, gqlutils.Unauthenticatedf(ctx, "authentication is required to request access")
}
if _, err := trustService.TrustCenterAccesses.Request(
ctx, scope,
&trust.TrustCenterAccessRequest{
TrustCenterID: trustCenter.ID,
IdentityID: identity.ID,
DocumentIDs: []gid.GID{},
ReportIDs: []gid.GID{input.ReportID},
TrustCenterFileIDs: []gid.GID{},
},
); err != nil {
r.logger.ErrorCtx(ctx, "cannot request report access", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.RequestReportAccessPayload{
Audit: types.NewAudit(audit),
}, nil
}
// RequestTrustCenterFileAccess is the resolver for the requestTrustCenterFileAccess field.
func (r *mutationResolver) RequestTrustCenterFileAccess(ctx context.Context, input types.RequestTrustCenterFileAccessInput) (*types.RequestFileAccessPayload, error) {
trustCenter := compliancepage.CompliancePageFromContext(ctx)
scope := coredata.NewScopeFromObjectID(trustCenter.ID)
trustService := r.trust
trustCenterFile, err := trustService.TrustCenterFiles.Get(ctx, scope, trustCenter.OrganizationID, input.TrustCenterFileID)
if err != nil {
if errors.Is(err, trust.ErrTrustCenterFileNotFound) || errors.Is(err, trust.ErrTrustCenterFileNotVisible) {
return nil, gqlutils.NotFoundf(ctx, "trust center file %q not found", input.TrustCenterFileID)
}
r.logger.ErrorCtx(ctx, "cannot load trust center file", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
if trustCenterFile.TrustCenterVisibility == coredata.TrustCenterVisibilityPublic {
return nil, gqlutils.Invalidf(
ctx,
"trust center file is publicly available and does not require access request",
)
}
identity := authn.IdentityFromContext(ctx)
if identity == nil {
return nil, gqlutils.Unauthenticatedf(ctx, "authentication is required to request access")
}
if _, err := trustService.TrustCenterAccesses.Request(
ctx, scope,
&trust.TrustCenterAccessRequest{
TrustCenterID: trustCenter.ID,
IdentityID: identity.ID,
DocumentIDs: []gid.GID{},
ReportIDs: []gid.GID{},
TrustCenterFileIDs: []gid.GID{input.TrustCenterFileID},
},
); err != nil {
r.logger.ErrorCtx(ctx, "cannot request trust center file access", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.RequestFileAccessPayload{
File: types.NewTrustCenterFile(trustCenterFile),
}, nil
}
// TotalCount is the resolver for the totalCount field.
func (r *subprocessorConnectionResolver) TotalCount(ctx context.Context, obj *types.SubprocessorConnection) (int, error) {
scope := coredata.NewScopeFromObjectID(obj.ParentID)
trustService := r.trust
switch obj.Resolver.(type) {
case *trustCenterResolver:
count, err := trustService.ThirdParties.CountForTrustCenterId(ctx, scope, obj.ParentID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count subprocessors", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
}
r.logger.ErrorCtx(ctx, "not implemented: TotalCount for parent type")
return 0, gqlutils.Internal(ctx)
}
// Logo is the resolver for the logo field.
func (r *trustCenterResolver) Logo(ctx context.Context, obj *types.TrustCenter) (*types.File, error) {
trustCenter := compliancepage.CompliancePageFromContext(ctx)
if trustCenter.LogoFileID == nil {
return nil, nil
}
return r.loadPublicFile(ctx, *trustCenter.LogoFileID)
}
// DarkLogo is the resolver for the darkLogo field.
func (r *trustCenterResolver) DarkLogo(ctx context.Context, obj *types.TrustCenter) (*types.File, error) {
trustCenter := compliancepage.CompliancePageFromContext(ctx)
if trustCenter.DarkLogoFileID == nil {
return nil, nil
}
return r.loadPublicFile(ctx, *trustCenter.DarkLogoFileID)
}
// NonDisclosureAgreement is the resolver for the nonDisclosureAgreement field.
func (r *trustCenterResolver) NonDisclosureAgreement(ctx context.Context, obj *types.TrustCenter) (*types.NonDisclosureAgreement, error) {
trustCenter := compliancepage.CompliancePageFromContext(ctx)
if trustCenter.NonDisclosureAgreementFileID == nil {
return nil, nil
}
scope := coredata.NewScopeFromObjectID(obj.ID)
trustService := r.trust
file, err := trustService.TrustCenters.GetNDAFile(ctx, scope, obj.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot load NDA file", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
if file == nil {
return nil, nil
}
return types.NewNonDisclosureAgreement(file), nil
}
// ViewerSubscription is the resolver for the viewerSubscription field.
func (r *trustCenterResolver) ViewerSubscription(ctx context.Context, obj *types.TrustCenter) (*types.MailingListSubscriber, error) {
trustCenter := compliancepage.CompliancePageFromContext(ctx)
if trustCenter.MailingListID == nil {
return nil, nil
}
identity := authn.IdentityFromContext(ctx)
if identity == nil {
return nil, nil
}
subscriber, err := r.mailman.GetSubscriber(ctx, *trustCenter.MailingListID, identity.EmailAddress)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot get mailing list subscription", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
if subscriber == nil {
return nil, nil
}
return types.NewMailingListSubscriber(subscriber), nil
}
// Organization is the resolver for the organization field.
func (r *trustCenterResolver) Organization(ctx context.Context, obj *types.TrustCenter) (*types.Organization, error) {
return obj.Organization, nil
}
// Documents is the resolver for the documents field.
func (r *trustCenterResolver) Documents(ctx context.Context, obj *types.TrustCenter, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.DocumentConnection, error) {
scope := coredata.NewScopeFromObjectID(obj.ID)
trustService := r.trust
pageOrderBy := page.OrderBy[coredata.DocumentOrderField]{
Field: coredata.DocumentOrderFieldTitle,
Direction: page.OrderDirectionAsc,
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
documentPage, err := trustService.Documents.ListForOrganizationId(ctx, scope, obj.Organization.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list public documents", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewDocumentConnection(documentPage), nil
}
// Audits is the resolver for the audits field.
func (r *trustCenterResolver) Audits(ctx context.Context, obj *types.TrustCenter, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.AuditConnection, error) {
scope := coredata.NewScopeFromObjectID(obj.ID)
trustService := r.trust
pageOrderBy := page.OrderBy[coredata.AuditOrderField]{
Field: coredata.AuditOrderFieldValidFrom,
Direction: page.OrderDirectionDesc,
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
auditPage, err := trustService.Audits.ListForOrganizationId(ctx, scope, obj.Organization.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list public audits", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewAuditConnection(auditPage), nil
}
// Subprocessors is the resolver for the subprocessors field.
func (r *trustCenterResolver) Subprocessors(ctx context.Context, obj *types.TrustCenter, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.SubprocessorConnection, error) {
scope := coredata.NewScopeFromObjectID(obj.ID)
trustService := r.trust
pageOrderBy := page.OrderBy[coredata.ThirdPartyOrderField]{
Field: coredata.ThirdPartyOrderFieldName,
Direction: page.OrderDirectionAsc,
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
thirdPartyPage, err := trustService.ThirdParties.ListForOrganizationId(ctx, scope, obj.Organization.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list subprocessors", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewSubprocessorConnection(thirdPartyPage, r, obj.ID), nil
}
// 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) (*types.TrustCenterReferenceConnection, error) {
scope := coredata.NewScopeFromObjectID(obj.ID)
trustService := r.trust
pageOrderBy := page.OrderBy[coredata.TrustCenterReferenceOrderField]{
Field: coredata.TrustCenterReferenceOrderFieldRank,
Direction: page.OrderDirectionAsc,
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
referencePage, err := trustService.TrustCenterReferences.ListForTrustCenterID(ctx, scope, obj.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list public trust center references", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewTrustCenterReferenceConnection(referencePage), nil
}
// TrustCenterFiles is the resolver for the trustCenterFiles field.
func (r *trustCenterResolver) TrustCenterFiles(ctx context.Context, obj *types.TrustCenter, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.TrustCenterFileConnection, error) {
scope := coredata.NewScopeFromObjectID(obj.ID)
trustService := r.trust
pageOrderBy := page.OrderBy[coredata.TrustCenterFileOrderField]{
Field: coredata.TrustCenterFileOrderFieldName,
Direction: page.OrderDirectionAsc,
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
filter := coredata.NewTrustCenterFileFilter(
coredata.WithTrustCenterFileVisibilities(
coredata.TrustCenterVisibilityPublic,
coredata.TrustCenterVisibilityPrivate,
),
)
trustCenterFilePage, err := trustService.TrustCenterFiles.ListForOrganizationId(ctx, scope, obj.Organization.ID, cursor, filter)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list public trust center files", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewTrustCenterFileConnection(trustCenterFilePage), nil
}
// 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) (*types.ComplianceFrameworkConnection, error) {
scope := coredata.NewScopeFromObjectID(obj.ID)
trustService := r.trust
pageOrderBy := page.OrderBy[coredata.ComplianceFrameworkOrderField]{
Field: coredata.ComplianceFrameworkOrderFieldRank,
Direction: page.OrderDirectionAsc,
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
cfPage, err := trustService.ComplianceFrameworks.ListByTrustCenterID(ctx, scope, obj.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list compliance frameworks", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewComplianceFrameworkConnection(cfPage), nil
}
// ExternalUrls is the resolver for the externalUrls field.
func (r *trustCenterResolver) ExternalUrls(ctx context.Context, obj *types.TrustCenter, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.ComplianceExternalURLConnection, error) {
scope := coredata.NewScopeFromObjectID(obj.ID)
trustService := r.trust
pageOrderBy := page.OrderBy[coredata.ComplianceExternalURLOrderField]{
Field: coredata.ComplianceExternalURLOrderFieldRank,
Direction: page.OrderDirectionAsc,
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
result, err := trustService.ComplianceExternalURLs.ListForTrustCenterID(ctx, scope, obj.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list compliance external URLs", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewComplianceExternalURLConnection(result), nil
}
// Updates is the resolver for the updates field.
func (r *trustCenterResolver) Updates(ctx context.Context, obj *types.TrustCenter, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.MailingListUpdateConnection, error) {
scope := coredata.NewScopeFromObjectID(obj.ID)
trustService := r.trust
tc, err := trustService.TrustCenters.Get(ctx, scope, obj.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot load trust center", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
if tc.MailingListID == nil {
return &types.MailingListUpdateConnection{Edges: []*types.MailingListUpdateEdge{}, PageInfo: &types.PageInfo{}}, nil
}
pageOrderBy := page.OrderBy[coredata.MailingListUpdateOrderField]{
Field: coredata.MailingListUpdateOrderFieldUpdatedAt,
Direction: page.OrderDirectionDesc,
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
result, err := r.mailman.ListSentMailingListUpdates(ctx, *tc.MailingListID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list mailing list updates", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewMailingListUpdateConnection(result), nil
}
// IsUserAuthorized is the resolver for the isUserAuthorized field.
func (r *trustCenterFileResolver) IsUserAuthorized(ctx context.Context, obj *types.TrustCenterFile) (bool, error) {
scope := coredata.NewScopeFromObjectID(obj.ID)
trustService := r.trust
trustCenter := compliancepage.CompliancePageFromContext(ctx)
trustCenterFile, err := trustService.TrustCenterFiles.Get(ctx, scope, trustCenter.OrganizationID, obj.ID)
if err != nil {
if errors.Is(err, trust.ErrTrustCenterFileNotFound) || errors.Is(err, trust.ErrTrustCenterFileNotVisible) {
return false, gqlutils.NotFoundf(ctx, "trust center file %q not found", obj.ID)
}
r.logger.ErrorCtx(ctx, "cannot load trust center file", log.Error(err))
return false, gqlutils.Internal(ctx)
}
if trustCenterFile.TrustCenterVisibility == coredata.TrustCenterVisibilityPublic {
return true, nil
}
identity := authn.IdentityFromContext(ctx)
if identity == nil {
return false, nil
}
fileAccess, err := trustService.TrustCenterAccesses.GetTrustCenterFileAccess(ctx, scope,
trustCenter.ID,
identity.ID,
obj.ID,
)
if err != nil {
if errors.Is(err, trust.ErrMembershipNotFound) ||
errors.Is(err, trust.ErrUserNotFound) ||
errors.Is(err, trust.ErrUserInactive) ||
errors.Is(err, trust.ErrDocumentAccessNotFound) {
return false, nil
}
r.logger.ErrorCtx(ctx, "cannot check trust center file access", log.Error(err))
return false, gqlutils.Internal(ctx)
}
return fileAccess.Status == coredata.TrustCenterDocumentAccessStatusGranted, nil
}
// Access is the resolver for the access field.
func (r *trustCenterFileResolver) Access(ctx context.Context, obj *types.TrustCenterFile) (*types.DocumentAccess, error) {
scope := coredata.NewScopeFromObjectID(obj.ID)
trustService := r.trust
trustCenter := compliancepage.CompliancePageFromContext(ctx)
identity := authn.IdentityFromContext(ctx)
if identity == nil {
return nil, nil // User is not authenticated, so no access requested
}
access, err := trustService.TrustCenterAccesses.GetTrustCenterFileAccess(
ctx, scope,
trustCenter.ID,
identity.ID,
obj.ID,
)
if err != nil {
if errors.Is(err, trust.ErrMembershipNotFound) ||
errors.Is(err, trust.ErrUserNotFound) ||
errors.Is(err, trust.ErrDocumentAccessNotFound) {
return nil, nil
}
if errors.Is(err, trust.ErrUserInactive) {
return nil, gqlutils.Forbidden(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot get file access", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DocumentAccess{
ID: access.ID,
Status: access.Status,
}, nil
}
// Logo is the resolver for the logo field.
func (r *trustCenterReferenceResolver) Logo(ctx context.Context, obj *types.TrustCenterReference) (*types.File, error) {
scope := coredata.NewScopeFromObjectID(obj.ID)
reference, err := r.trust.TrustCenterReferences.Get(ctx, scope, obj.ID)
if err != nil {
return nil, gqlutils.NotFoundf(ctx, "trust center reference %q not found", obj.ID)
}
return r.loadPublicFile(ctx, reference.LogoFileID)
}
// Audit returns schema.AuditResolver implementation.
func (r *Resolver) Audit() schema.AuditResolver { return &auditResolver{r} }
// AuditReport returns schema.AuditReportResolver implementation.
func (r *Resolver) AuditReport() schema.AuditReportResolver { return &auditReportResolver{r} }
// ComplianceFramework returns schema.ComplianceFrameworkResolver implementation.
func (r *Resolver) ComplianceFramework() schema.ComplianceFrameworkResolver {
return &complianceFrameworkResolver{r}
}
// Document returns schema.DocumentResolver implementation.
func (r *Resolver) Document() schema.DocumentResolver { return &documentResolver{r} }
// Framework returns schema.FrameworkResolver implementation.
func (r *Resolver) Framework() schema.FrameworkResolver { return &frameworkResolver{r} }
// SubprocessorConnection returns schema.SubprocessorConnectionResolver implementation.
func (r *Resolver) SubprocessorConnection() schema.SubprocessorConnectionResolver {
return &subprocessorConnectionResolver{r}
}
// TrustCenter returns schema.TrustCenterResolver implementation.
func (r *Resolver) TrustCenter() schema.TrustCenterResolver { return &trustCenterResolver{r} }
// TrustCenterFile returns schema.TrustCenterFileResolver implementation.
func (r *Resolver) TrustCenterFile() schema.TrustCenterFileResolver {
return &trustCenterFileResolver{r}
}
// TrustCenterReference returns schema.TrustCenterReferenceResolver implementation.
func (r *Resolver) TrustCenterReference() schema.TrustCenterReferenceResolver {
return &trustCenterReferenceResolver{r}
}
type auditResolver struct{ *Resolver }
type auditReportResolver struct{ *Resolver }
type complianceFrameworkResolver struct{ *Resolver }
type documentResolver struct{ *Resolver }
type frameworkResolver struct{ *Resolver }
type subprocessorConnectionResolver struct{ *Resolver }
type trustCenterResolver struct{ *Resolver }
type trustCenterFileResolver struct{ *Resolver }
type trustCenterReferenceResolver struct{ *Resolver }