Add trust center front

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
Sacha Al Himdani
2025-07-28 17:24:16 +02:00
parent 2ac8f31492
commit cb3d79502d
72 changed files with 18712 additions and 82 deletions

View File

@@ -0,0 +1,99 @@
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
//
// 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 trust
import (
"context"
"fmt"
"time"
"github.com/getprobo/probo/pkg/coredata"
"github.com/getprobo/probo/pkg/gid"
"github.com/getprobo/probo/pkg/page"
"go.gearno.de/kit/pg"
)
type AuditService struct {
svc *TenantService
}
func (s AuditService) Get(
ctx context.Context,
auditID gid.GID,
) (*coredata.Audit, error) {
audit := &coredata.Audit{}
err := s.svc.pg.WithConn(
ctx,
func(conn pg.Conn) error {
return audit.LoadByID(ctx, conn, s.svc.scope, auditID)
},
)
if err != nil {
return nil, err
}
return audit, nil
}
func (s AuditService) ListForOrganizationId(
ctx context.Context,
organizationID gid.GID,
cursor *page.Cursor[coredata.AuditOrderField],
) (*page.Page[*coredata.Audit, coredata.AuditOrderField], error) {
var audits coredata.Audits
err := s.svc.pg.WithConn(
ctx,
func(conn pg.Conn) error {
filter := coredata.NewAuditTrustCenterFilter()
err := audits.LoadByOrganizationID(ctx, conn, s.svc.scope, organizationID, cursor, filter)
if err != nil {
return fmt.Errorf("cannot load audits: %w", err)
}
return nil
},
)
if err != nil {
return nil, err
}
return page.NewPage(audits, cursor), nil
}
func (s AuditService) GenerateReportURL(
ctx context.Context,
auditID gid.GID,
expiresIn time.Duration,
) (*string, error) {
audit, err := s.Get(ctx, auditID)
if err != nil {
return nil, fmt.Errorf("cannot get audit: %w", err)
}
if audit.ReportID == nil {
return nil, fmt.Errorf("audit has no report")
}
url, err := s.svc.Reports.GenerateDownloadURL(ctx, *audit.ReportID, expiresIn)
if err != nil {
return nil, fmt.Errorf("cannot generate report download URL: %w", err)
}
return url, nil
}

View File

@@ -0,0 +1,219 @@
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
//
// 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 trust
import (
"context"
"fmt"
"io"
"github.com/getprobo/probo/pkg/coredata"
"github.com/getprobo/probo/pkg/docgen"
"github.com/getprobo/probo/pkg/gid"
"github.com/getprobo/probo/pkg/html2pdf"
"github.com/getprobo/probo/pkg/page"
"go.gearno.de/kit/pg"
)
type (
DocumentService struct {
svc *TenantService
html2pdfConverter *html2pdf.Converter
}
)
// ListVersions lists all versions of a document
func (s *DocumentService) ListVersions(
ctx context.Context,
documentID gid.GID,
cursor *page.Cursor[coredata.DocumentVersionOrderField],
) (*page.Page[*coredata.DocumentVersion, coredata.DocumentVersionOrderField], error) {
var documentVersions coredata.DocumentVersions
err := s.svc.pg.WithConn(
ctx,
func(conn pg.Conn) error {
return documentVersions.LoadByDocumentID(ctx, conn, s.svc.scope, documentID, cursor)
},
)
if err != nil {
return nil, err
}
return page.NewPage(documentVersions, cursor), nil
}
func (s *DocumentService) ListForOrganizationId(
ctx context.Context,
organizationID gid.GID,
cursor *page.Cursor[coredata.DocumentOrderField],
) (*page.Page[*coredata.Document, coredata.DocumentOrderField], error) {
var documents coredata.Documents
err := s.svc.pg.WithConn(
ctx,
func(conn pg.Conn) error {
filter := coredata.NewDocumentTrustCenterFilter()
err := documents.LoadByOrganizationID(ctx, conn, s.svc.scope, organizationID, cursor, filter)
if err != nil {
return fmt.Errorf("cannot load documents: %w", err)
}
return nil
},
)
if err != nil {
return nil, err
}
return page.NewPage(documents, cursor), nil
}
func (s *DocumentService) ExportPDF(
ctx context.Context,
documentVersionID gid.GID,
) ([]byte, error) {
document := &coredata.Document{}
version := &coredata.DocumentVersion{}
owner := &coredata.People{}
publishedBy := &coredata.People{}
signatures := coredata.DocumentVersionSignatures{}
peopleMap := make(map[gid.GID]*coredata.People)
err := s.svc.pg.WithConn(
ctx,
func(conn pg.Conn) error {
if err := version.LoadByID(ctx, conn, s.svc.scope, documentVersionID); err != nil {
return fmt.Errorf("cannot load document version: %w", err)
}
if err := document.LoadByID(ctx, conn, s.svc.scope, version.DocumentID); err != nil {
return fmt.Errorf("cannot load document: %w", err)
}
if !document.ShowOnTrustCenter {
return fmt.Errorf("document not visible on trust center")
}
if version.PublishedBy != nil {
if err := publishedBy.LoadByID(ctx, conn, s.svc.scope, *version.PublishedBy); err != nil {
return fmt.Errorf("cannot load published by person: %w", err)
}
}
cursor := page.NewCursor(
100,
nil,
page.Head,
page.OrderBy[coredata.DocumentVersionSignatureOrderField]{
Field: coredata.DocumentVersionSignatureOrderFieldCreatedAt,
Direction: page.OrderDirectionAsc,
},
)
if err := signatures.LoadByDocumentVersionID(ctx, conn, s.svc.scope, documentVersionID, cursor); err != nil {
return fmt.Errorf("cannot load document version signatures: %w", err)
}
if err := owner.LoadByID(ctx, conn, s.svc.scope, document.OwnerID); err != nil {
return fmt.Errorf("cannot load document owner: %w", err)
}
// TODO: refactor this to use a single query
for _, sig := range signatures {
if _, ok := peopleMap[sig.SignedBy]; !ok {
people := &coredata.People{}
if err := people.LoadByID(ctx, conn, s.svc.scope, sig.SignedBy); err != nil {
return fmt.Errorf("cannot load people %q: %w", sig.SignedBy, err)
}
peopleMap[sig.SignedBy] = people
}
if _, ok := peopleMap[sig.RequestedBy]; !ok {
people := &coredata.People{}
if err := people.LoadByID(ctx, conn, s.svc.scope, sig.RequestedBy); err != nil {
return fmt.Errorf("cannot load people %q: %w", sig.RequestedBy, err)
}
peopleMap[sig.RequestedBy] = people
}
}
return nil
},
)
if err != nil {
return nil, err
}
classification := docgen.ClassificationInternal
switch document.DocumentType {
case coredata.DocumentTypePolicy:
classification = docgen.ClassificationConfidential
case coredata.DocumentTypeISMS:
classification = docgen.ClassificationSecret
}
docData := docgen.DocumentData{
Title: version.Title,
Content: version.Content,
Version: version.VersionNumber,
Classification: classification,
Approver: owner.FullName,
Description: version.Changelog,
PublishedAt: version.PublishedAt,
PublishedBy: publishedBy.FullName,
Signatures: make([]docgen.SignatureData, len(signatures)),
}
for i, sig := range signatures {
docData.Signatures[i] = docgen.SignatureData{
SignedBy: peopleMap[sig.SignedBy].FullName,
SignedAt: sig.SignedAt,
State: sig.State,
RequestedAt: sig.RequestedAt,
RequestedBy: peopleMap[sig.RequestedBy].FullName,
}
}
htmlContent, err := docgen.RenderHTML(docData)
if err != nil {
return nil, fmt.Errorf("cannot generate HTML: %w", err)
}
cfg := html2pdf.RenderConfig{
PageFormat: html2pdf.PageFormatA4,
Orientation: html2pdf.OrientationPortrait,
MarginTop: html2pdf.NewMarginInches(1.0),
MarginBottom: html2pdf.NewMarginInches(1.0),
MarginLeft: html2pdf.NewMarginInches(1.0),
MarginRight: html2pdf.NewMarginInches(1.0),
PrintBackground: true,
Scale: 1.0,
}
pdfReader, err := s.html2pdfConverter.GeneratePDF(ctx, htmlContent, cfg)
if err != nil {
return nil, fmt.Errorf("cannot generate PDF: %w", err)
}
pdfData, err := io.ReadAll(pdfReader)
if err != nil {
return nil, fmt.Errorf("cannot read PDF data: %w", err)
}
return pdfData, nil
}

View File

@@ -0,0 +1,51 @@
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
//
// 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 trust
import (
"context"
"fmt"
"github.com/getprobo/probo/pkg/coredata"
"github.com/getprobo/probo/pkg/gid"
"go.gearno.de/kit/pg"
)
type FrameworkService struct {
svc *TenantService
}
func (s FrameworkService) Get(
ctx context.Context,
frameworkID gid.GID,
) (*coredata.Framework, error) {
framework := &coredata.Framework{}
err := s.svc.pg.WithConn(ctx, func(conn pg.Conn) error {
err := framework.LoadByID(ctx, conn, s.svc.scope, frameworkID)
if err != nil {
return fmt.Errorf("cannot load framework: %w", err)
}
return nil
})
if err != nil {
return nil, err
}
return framework, nil
}

View File

@@ -0,0 +1,97 @@
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
//
// 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 trust
import (
"context"
"fmt"
"net/url"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/getprobo/probo/pkg/coredata"
"github.com/getprobo/probo/pkg/gid"
"go.gearno.de/kit/pg"
)
type OrganizationService struct {
svc *TenantService
}
func (s OrganizationService) Get(
ctx context.Context,
organizationID gid.GID,
) (*coredata.Organization, error) {
organization := &coredata.Organization{}
err := s.svc.pg.WithConn(
ctx,
func(conn pg.Conn) error {
err := organization.LoadByID(
ctx,
conn,
s.svc.scope,
organizationID,
)
if err != nil {
return fmt.Errorf("cannot load organization: %w", err)
}
return nil
},
)
if err != nil {
return nil, err
}
return organization, nil
}
func (s OrganizationService) GenerateLogoURL(
ctx context.Context,
organizationID gid.GID,
expiresIn time.Duration,
) (*string, error) {
organization, err := s.Get(ctx, organizationID)
if err != nil {
return nil, fmt.Errorf("cannot get organization: %w", err)
}
if organization.LogoObjectKey == "" {
return nil, nil
}
presignClient := s3.NewPresignClient(s.svc.s3)
encodedFilename := url.QueryEscape(organization.Name)
contentDisposition := fmt.Sprintf("attachment; filename=\"%s\"; filename*=UTF-8''%s",
encodedFilename, encodedFilename)
presignedReq, err := presignClient.PresignGetObject(ctx, &s3.GetObjectInput{
Bucket: aws.String(s.svc.bucket),
Key: aws.String(organization.LogoObjectKey),
ResponseCacheControl: aws.String("max-age=3600, public"),
ResponseContentDisposition: aws.String(contentDisposition),
}, func(opts *s3.PresignOptions) {
opts.Expires = expiresIn
})
if err != nil {
return nil, fmt.Errorf("cannot presign GetObject request: %w", err)
}
return &presignedReq.URL, nil
}

View File

@@ -0,0 +1,84 @@
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
//
// 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 trust
import (
"context"
"fmt"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/getprobo/probo/pkg/coredata"
"github.com/getprobo/probo/pkg/gid"
"go.gearno.de/kit/pg"
)
type ReportService struct {
svc *TenantService
}
func (s ReportService) Get(
ctx context.Context,
reportID gid.GID,
) (*coredata.Report, error) {
report := &coredata.Report{}
err := s.svc.pg.WithConn(
ctx,
func(conn pg.Conn) error {
err := report.LoadByID(ctx, conn, s.svc.scope, reportID)
if err != nil {
return fmt.Errorf("cannot load report: %w", err)
}
return nil
},
)
if err != nil {
return nil, err
}
return report, nil
}
func (s ReportService) GenerateDownloadURL(
ctx context.Context,
reportID gid.GID,
expiresIn time.Duration,
) (*string, error) {
report, err := s.Get(ctx, reportID)
if err != nil {
return nil, fmt.Errorf("cannot get report: %w", err)
}
presignClient := s3.NewPresignClient(s.svc.s3)
presignedReq, err := presignClient.PresignGetObject(ctx, &s3.GetObjectInput{
Bucket: aws.String(s.svc.bucket),
Key: aws.String(report.ObjectKey),
ResponseCacheControl: aws.String("max-age=3600, public"),
ResponseContentType: aws.String(report.MimeType),
ResponseContentDisposition: aws.String(fmt.Sprintf("attachment; filename=\"%s\"", report.Filename)),
}, func(opts *s3.PresignOptions) {
opts.Expires = expiresIn
})
if err != nil {
return nil, fmt.Errorf("cannot presign GetObject request: %w", err)
}
return &presignedReq.URL, nil
}

108
pkg/trust/service.go Normal file
View File

@@ -0,0 +1,108 @@
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
//
// 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 trust
import (
"github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/getprobo/probo/pkg/coredata"
"github.com/getprobo/probo/pkg/crypto/cipher"
"github.com/getprobo/probo/pkg/gid"
"github.com/getprobo/probo/pkg/html2pdf"
"github.com/getprobo/probo/pkg/probo"
"github.com/getprobo/probo/pkg/usrmgr"
"go.gearno.de/kit/pg"
)
type (
Service struct {
pg *pg.Client
s3 *s3.Client
bucket string
proboSvc *probo.Service
encryptionKey cipher.EncryptionKey
tokenSecret string
usrmgr *usrmgr.Service
html2pdfConverter *html2pdf.Converter
}
TenantService struct {
pg *pg.Client
s3 *s3.Client
bucket string
scope coredata.Scoper
proboSvc *probo.Service
encryptionKey cipher.EncryptionKey
tokenSecret string
usrmgr *usrmgr.Service
html2pdfConverter *html2pdf.Converter
TrustCenters *TrustCenterService
Documents *DocumentService
Audits *AuditService
Vendors *VendorService
Frameworks *FrameworkService
TrustCenterAccesses *TrustCenterAccessService
Reports *ReportService
Organizations *OrganizationService
}
)
func NewService(
pgClient *pg.Client,
s3Client *s3.Client,
bucket string,
encryptionKey cipher.EncryptionKey,
tokenSecret string,
usrmgr *usrmgr.Service,
html2pdfConverter *html2pdf.Converter,
) *Service {
return &Service{
pg: pgClient,
s3: s3Client,
bucket: bucket,
encryptionKey: encryptionKey,
tokenSecret: tokenSecret,
usrmgr: usrmgr,
html2pdfConverter: html2pdfConverter,
}
}
func (s *Service) GetEncryptionKey() cipher.EncryptionKey {
return s.encryptionKey
}
func (s *Service) WithTenant(tenantID gid.TenantID) *TenantService {
tenantService := &TenantService{
pg: s.pg,
s3: s.s3,
bucket: s.bucket,
scope: coredata.NewScope(tenantID),
proboSvc: s.proboSvc,
encryptionKey: s.encryptionKey,
tokenSecret: s.tokenSecret,
usrmgr: s.usrmgr,
html2pdfConverter: s.html2pdfConverter,
}
tenantService.TrustCenters = &TrustCenterService{svc: tenantService}
tenantService.Documents = &DocumentService{svc: tenantService, html2pdfConverter: s.html2pdfConverter}
tenantService.Audits = &AuditService{svc: tenantService}
tenantService.Vendors = &VendorService{svc: tenantService}
tenantService.Frameworks = &FrameworkService{svc: tenantService}
tenantService.TrustCenterAccesses = &TrustCenterAccessService{svc: tenantService, usrmgr: s.usrmgr}
tenantService.Reports = &ReportService{svc: tenantService}
tenantService.Organizations = &OrganizationService{svc: tenantService}
return tenantService
}

View File

@@ -0,0 +1,89 @@
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
//
// 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 trust
import (
"context"
"fmt"
"github.com/getprobo/probo/pkg/coredata"
"github.com/getprobo/probo/pkg/gid"
"github.com/getprobo/probo/pkg/probo"
"github.com/getprobo/probo/pkg/statelesstoken"
"github.com/getprobo/probo/pkg/usrmgr"
"go.gearno.de/kit/pg"
)
type (
TrustCenterAccessService struct {
svc *TenantService
usrmgr *usrmgr.Service
}
)
const (
TokenTypeTrustCenterAccess = "trust_center_access"
)
func (s TrustCenterAccessService) ValidateToken(
ctx context.Context,
tokenString string,
) (*probo.TrustCenterAccessData, error) {
token, err := statelesstoken.ValidateToken[probo.TrustCenterAccessData](
s.svc.tokenSecret,
TokenTypeTrustCenterAccess,
tokenString,
)
if err != nil {
return nil, fmt.Errorf("cannot validate trust center access token: %w", err)
}
access := &coredata.TrustCenterAccess{}
err = s.svc.pg.WithConn(ctx, func(conn pg.Conn) error {
err := access.LoadByTrustCenterIDAndEmail(ctx, conn, s.svc.scope, token.Data.TrustCenterID, token.Data.Email)
if err != nil {
return fmt.Errorf("cannot load trust center access: %w", err)
}
return nil
})
if err != nil {
return nil, err
}
if !access.Active {
return nil, fmt.Errorf("access has been revoked")
}
return &token.Data, nil
}
func (s TrustCenterAccessService) IsAccessActive(
ctx context.Context,
trustCenterID gid.GID,
email string,
) (bool, error) {
access := &coredata.TrustCenterAccess{}
err := s.svc.pg.WithConn(ctx, func(conn pg.Conn) error {
return access.LoadByTrustCenterIDAndEmail(ctx, conn, s.svc.scope, trustCenterID, email)
})
if err != nil {
return false, fmt.Errorf("cannot load trust center access: %w", err)
}
return access.Active, nil
}

View File

@@ -0,0 +1,53 @@
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
//
// 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 trust
import (
"context"
"fmt"
"github.com/getprobo/probo/pkg/coredata"
"go.gearno.de/kit/pg"
)
type TrustCenterService struct {
svc *TenantService
}
func (s TrustCenterService) GetBySlug(
ctx context.Context,
slug string,
) (*coredata.TrustCenter, error) {
trustCenter := &coredata.TrustCenter{}
err := s.svc.pg.WithConn(
ctx,
func(conn pg.Conn) error {
err := trustCenter.LoadBySlug(ctx, conn, slug)
if err != nil {
return fmt.Errorf("cannot load trust center: %w", err)
}
return nil
},
)
if err != nil {
return nil, err
}
return trustCenter, nil
}

View File

@@ -0,0 +1,81 @@
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
//
// 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 trust
import (
"context"
"fmt"
"github.com/getprobo/probo/pkg/coredata"
"github.com/getprobo/probo/pkg/gid"
"github.com/getprobo/probo/pkg/page"
"go.gearno.de/kit/pg"
)
type VendorService struct {
svc *TenantService
}
func (s VendorService) Get(
ctx context.Context,
vendorID gid.GID,
) (*coredata.Vendor, error) {
vendor := &coredata.Vendor{}
err := s.svc.pg.WithConn(
ctx,
func(conn pg.Conn) error {
err := vendor.LoadByID(ctx, conn, s.svc.scope, vendorID)
if err != nil {
return fmt.Errorf("cannot load vendor: %w", err)
}
return nil
},
)
if err != nil {
return nil, err
}
return vendor, nil
}
func (s VendorService) ListForOrganizationId(
ctx context.Context,
organizationID gid.GID,
cursor *page.Cursor[coredata.VendorOrderField],
) (*page.Page[*coredata.Vendor, coredata.VendorOrderField], error) {
var vendors coredata.Vendors
err := s.svc.pg.WithConn(
ctx,
func(conn pg.Conn) error {
filter := coredata.NewVendorTrustCenterFilter()
err := vendors.LoadByOrganizationID(ctx, conn, s.svc.scope, organizationID, cursor, filter)
if err != nil {
return fmt.Errorf("cannot load vendors: %w", err)
}
return nil
},
)
if err != nil {
return nil, err
}
return page.NewPage(vendors, cursor), nil
}