Files
probo/pkg/trust/trust_center_access_service.go
Sacha Al Himdani b06bd113f3 Add nda to trust center
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2025-09-15 16:53:51 +02:00

171 lines
5.1 KiB
Go

// 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"
"encoding/json"
"errors"
"fmt"
"net/mail"
"time"
"github.com/getprobo/probo/pkg/coredata"
"github.com/getprobo/probo/pkg/gid"
"github.com/getprobo/probo/pkg/usrmgr"
"go.gearno.de/kit/pg"
)
type (
TrustCenterAccessService struct {
svc *TenantService
usrmgr *usrmgr.Service
}
CreateTrustCenterAccessRequest struct {
TrustCenterID gid.GID
Email string
Name string
}
)
const (
TokenTypeTrustCenterAccess = "trust_center_access"
)
func (s TrustCenterAccessService) ValidateToken(
ctx context.Context,
trustCenterID gid.GID,
email string,
) error {
return s.svc.pg.WithConn(ctx, func(conn pg.Conn) error {
access := &coredata.TrustCenterAccess{}
err := access.LoadByTrustCenterIDAndEmail(ctx, conn, s.svc.scope, trustCenterID, email)
if err != nil {
return fmt.Errorf("cannot load trust center access: %w", err)
}
if !access.Active {
return fmt.Errorf("trust center access is not active")
}
return nil
})
}
func (s TrustCenterAccessService) Create(
ctx context.Context,
req *CreateTrustCenterAccessRequest,
) (*coredata.TrustCenterAccess, error) {
if _, err := mail.ParseAddress(req.Email); err != nil {
return nil, fmt.Errorf("invalid email address")
}
if req.Name == "" {
return nil, fmt.Errorf("name is required")
}
now := time.Now()
var access *coredata.TrustCenterAccess
err := s.svc.pg.WithTx(ctx, func(tx pg.Conn) error {
existingAccess := &coredata.TrustCenterAccess{}
err := existingAccess.LoadByTrustCenterIDAndEmail(ctx, tx, s.svc.scope, req.TrustCenterID, req.Email)
if err == nil {
if existingAccess.Active {
return fmt.Errorf("active trust center access already exists for this email")
}
if err := existingAccess.Delete(ctx, tx, s.svc.scope); err != nil {
return fmt.Errorf("cannot delete existing trust center access: %w", err)
}
} else {
var notFoundErr *coredata.ErrTrustCenterAccessNotFound
if !errors.As(err, &notFoundErr) {
return fmt.Errorf("cannot load trust center access: %w", err)
}
}
access = &coredata.TrustCenterAccess{
ID: gid.New(s.svc.scope.GetTenantID(), coredata.TrustCenterAccessEntityType),
TenantID: s.svc.scope.GetTenantID(),
TrustCenterID: req.TrustCenterID,
Email: req.Email,
Name: req.Name,
Active: false,
HasAcceptedNonDisclosureAgreement: false,
CreatedAt: now,
UpdatedAt: now,
}
if err := access.Insert(ctx, tx, s.svc.scope); err != nil {
return fmt.Errorf("cannot insert trust center access: %w", err)
}
return nil
})
if err != nil {
return nil, err
}
return access, nil
}
func (s TrustCenterAccessService) HasAcceptedNonDisclosureAgreement(ctx context.Context, trustCenterID gid.GID, email string) (bool, error) {
access := &coredata.TrustCenterAccess{}
err := s.svc.pg.WithConn(ctx, func(conn pg.Conn) error {
err := access.LoadByTrustCenterIDAndEmail(ctx, conn, s.svc.scope, trustCenterID, email)
if err != nil {
return fmt.Errorf("cannot load trust center access: %w", err)
}
return nil
})
if err != nil {
return false, nil
}
return access.HasAcceptedNonDisclosureAgreement, nil
}
func (s TrustCenterAccessService) AcceptNonDisclosureAgreement(ctx context.Context, trustCenterID gid.GID, email string) error {
return s.svc.pg.WithTx(ctx, func(tx pg.Conn) error {
access := &coredata.TrustCenterAccess{}
if err := access.LoadByTrustCenterIDAndEmail(ctx, tx, s.svc.scope, trustCenterID, email); err != nil {
return fmt.Errorf("cannot load trust center access: %w", err)
}
acceptationLogs, err := json.Marshal(map[string]string{
"email": email,
"timestamp": time.Now().Format(time.RFC3339),
"ip": ctx.Value(coredata.ContextKeyIPAddress).(string),
})
if err != nil {
return fmt.Errorf("cannot marshal non disclosure agreement acceptation logs: %w", err)
}
access.HasAcceptedNonDisclosureAgreement = true
access.UpdatedAt = time.Now()
access.HasAcceptedNonDisclosureAgreementMetadata = acceptationLogs
if err := access.Update(ctx, tx, s.svc.scope); err != nil {
return fmt.Errorf("cannot update trust center access: %w", err)
}
return nil
})
}