From e49da8b0d9a2661b9d764354cfe3b450c12631ec Mon Sep 17 00:00:00 2001 From: Sacha Al Himdani Date: Tue, 5 Aug 2025 14:07:57 +0200 Subject: [PATCH] Check tenant access Signed-off-by: Sacha Al Himdani --- .../src/pages/PublicTrustCenterPage.tsx | 11 +++++--- pkg/server/api/trust/v1/auth/auth.go | 27 +++++++++++++++++++ pkg/server/api/trust/v1/v1_resolver.go | 17 ++++++++++-- 3 files changed, 49 insertions(+), 6 deletions(-) diff --git a/apps/console/src/pages/PublicTrustCenterPage.tsx b/apps/console/src/pages/PublicTrustCenterPage.tsx index 825d1d949..c760f38c7 100644 --- a/apps/console/src/pages/PublicTrustCenterPage.tsx +++ b/apps/console/src/pages/PublicTrustCenterPage.tsx @@ -129,11 +129,14 @@ function PublicTrustCenterContent() { }) .then(response => response.json()) .then((result: GraphQLResponse) => { - if (result.errors && result.errors.some((error: GraphQLError) => - !error.message.includes('access denied: authentication required') - )) { - throw new Error(result.errors[0].message); + const nonAuthErrors = result.errors?.filter((error: GraphQLError) => + !error.message.includes('access denied') + ) || []; + + if (nonAuthErrors.length > 0) { + throw new Error(nonAuthErrors[0].message); } + setData(result.data || null); }) .catch(setError) diff --git a/pkg/server/api/trust/v1/auth/auth.go b/pkg/server/api/trust/v1/auth/auth.go index ee2e1b479..706e70754 100644 --- a/pkg/server/api/trust/v1/auth/auth.go +++ b/pkg/server/api/trust/v1/auth/auth.go @@ -36,6 +36,33 @@ type ContextAccessor interface { TokenAccessFromContext(ctx context.Context) *TokenAccessData } +func ValidateTenantAccess(ctx context.Context, accessor ContextAccessor, userTenantContextKey interface{}, resourceTenantID gid.TenantID) error { + tokenAccess := accessor.TokenAccessFromContext(ctx) + if tokenAccess != nil { + if tokenAccess.TenantID != resourceTenantID { + return fmt.Errorf("access denied: token not authorized for this organization") + } + return nil + } + + user := accessor.UserFromContext(ctx) + if user != nil { + userTenants, ok := ctx.Value(userTenantContextKey).(*[]gid.TenantID) + if !ok || userTenants == nil { + return fmt.Errorf("access denied: no tenant information available") + } + + for _, tenantID := range *userTenants { + if tenantID == resourceTenantID { + return nil + } + } + return fmt.Errorf("access denied: not authorized for this organization") + } + + return fmt.Errorf("access denied: authentication required") +} + func GetCurrentUserRole(ctx context.Context, accessor ContextAccessor) types.Role { user := accessor.UserFromContext(ctx) tokenAccess := accessor.TokenAccessFromContext(ctx) diff --git a/pkg/server/api/trust/v1/v1_resolver.go b/pkg/server/api/trust/v1/v1_resolver.go index 4dbe316b7..f9becea00 100644 --- a/pkg/server/api/trust/v1/v1_resolver.go +++ b/pkg/server/api/trust/v1/v1_resolver.go @@ -13,6 +13,7 @@ import ( "github.com/getprobo/probo/pkg/coredata" "github.com/getprobo/probo/pkg/gid" "github.com/getprobo/probo/pkg/page" + "github.com/getprobo/probo/pkg/server/api/trust/v1/auth" "github.com/getprobo/probo/pkg/server/api/trust/v1/schema" "github.com/getprobo/probo/pkg/server/api/trust/v1/types" ) @@ -57,6 +58,10 @@ func (r *auditResolver) Report(ctx context.Context, obj *types.Audit) (*types.Re // ReportURL is the resolver for the reportUrl field. func (r *auditResolver) ReportURL(ctx context.Context, obj *types.Audit) (*string, error) { + if err := auth.ValidateTenantAccess(ctx, r, userTenantContextKey, obj.ID.TenantID()); err != nil { + return nil, err + } + trust := r.TrustService(ctx, obj.ID.TenantID()) audit, err := trust.Audits.Get(ctx, obj.ID) @@ -78,6 +83,10 @@ func (r *auditResolver) ReportURL(ctx context.Context, obj *types.Audit) (*strin // ExportDocumentPDF is the resolver for the exportDocumentPDF field. func (r *mutationResolver) ExportDocumentPDF(ctx context.Context, input types.ExportDocumentPDFInput) (*types.ExportDocumentPDFPayload, error) { + if err := auth.ValidateTenantAccess(ctx, r, userTenantContextKey, input.DocumentID.TenantID()); err != nil { + return nil, err + } + trust := r.trustCenterSvc.WithTenant(input.DocumentID.TenantID()) pdf, err := trust.Documents.ExportPDF(ctx, input.DocumentID) @@ -99,9 +108,9 @@ func (r *organizationResolver) LogoURL(ctx context.Context, obj *types.Organizat // TrustCenterBySlug is the resolver for the trustCenterBySlug field. func (r *queryResolver) TrustCenterBySlug(ctx context.Context, slug string) (*types.TrustCenter, error) { - trust := r.trustCenterSvc.WithTenant(gid.NewTenantID()) + publicTrust := r.trustCenterSvc.WithTenant(gid.NewTenantID()) - trustCenter, err := trust.TrustCenters.GetBySlug(ctx, slug) + trustCenter, err := publicTrust.TrustCenters.GetBySlug(ctx, slug) if err != nil { return nil, nil } @@ -125,6 +134,10 @@ func (r *queryResolver) TrustCenterBySlug(ctx context.Context, slug string) (*ty // DownloadURL is the resolver for the downloadUrl field. func (r *reportResolver) DownloadURL(ctx context.Context, obj *types.Report) (*string, error) { + if err := auth.ValidateTenantAccess(ctx, r, userTenantContextKey, obj.ID.TenantID()); err != nil { + return nil, err + } + trust := r.TrustService(ctx, obj.ID.TenantID()) url, err := trust.Reports.GenerateDownloadURL(ctx, obj.ID, 5*time.Minute)