diff --git a/pkg/server/api/compliancepage/membership_provisioning_middleware.go b/pkg/server/api/compliancepage/member_provisioning_middleware.go similarity index 85% rename from pkg/server/api/compliancepage/membership_provisioning_middleware.go rename to pkg/server/api/compliancepage/member_provisioning_middleware.go index c593dae57..587a0b1aa 100644 --- a/pkg/server/api/compliancepage/membership_provisioning_middleware.go +++ b/pkg/server/api/compliancepage/member_provisioning_middleware.go @@ -26,7 +26,7 @@ import ( "go.probo.inc/probo/pkg/trust" ) -func NewMembershipProvisioningMiddleware(trustSvc *trust.Service, logger *log.Logger) func(next http.Handler) http.Handler { +func NewMemberProvisioningMiddleware(trustSvc *trust.Service, logger *log.Logger) func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { @@ -40,11 +40,7 @@ func NewMembershipProvisioningMiddleware(trustSvc *trust.Service, logger *log.Lo compliancePage := CompliancePageFromContext(r.Context()) - if _, err := trustSvc. - WithTenant(compliancePage.TenantID). - TrustCenterAccesses. - EnsureAccess(ctx, compliancePage.ID, identity.ID); err != nil { - + if _, err := trustSvc.ProvisionMember(ctx, compliancePage.ID, identity.ID); err != nil { httpserver.RenderJSON( w, http.StatusInternalServerError, diff --git a/pkg/server/api/trust/v1/resolver.go b/pkg/server/api/trust/v1/resolver.go index eee7f1893..5ce793909 100644 --- a/pkg/server/api/trust/v1/resolver.go +++ b/pkg/server/api/trust/v1/resolver.go @@ -88,7 +88,7 @@ func NewMux( r.Use(compliancepage.NewCompliancePagePresenceMiddleware()) r.Use(authn.NewSessionMiddleware(iamSvc, cookieConfig)) - r.Use(compliancepage.NewMembershipProvisioningMiddleware(trustSvc, logger)) + r.Use(compliancepage.NewMemberProvisioningMiddleware(trustSvc, logger)) graphqlHandler := NewGraphQLHandler(iamSvc, trustSvc, esignSvc, mailmanSvc, logger, baseURL, cookieConfig, tokenSecret) r.Handle("/graphql", graphqlHandler) diff --git a/pkg/server/api/trust/v1/v1_resolver.go b/pkg/server/api/trust/v1/v1_resolver.go index f155a17ae..2b4293fc9 100644 --- a/pkg/server/api/trust/v1/v1_resolver.go +++ b/pkg/server/api/trust/v1/v1_resolver.go @@ -259,10 +259,10 @@ func (r *mutationResolver) VerifyMagicLink(ctx context.Context, input types.Veri } trustCenter := compliancepage.CompliancePageFromContext(ctx) - trustService := r.TrustService(ctx, trustCenter.ID.TenantID()) - if _, err := trustService.TrustCenterAccesses.EnsureAccess(ctx, trustCenter.ID, identity.ID); err != nil { - r.logger.ErrorCtx(ctx, "cannot ensure trust center access", log.Error(err)) + if _, err := r.trust.ProvisionMember(ctx, trustCenter.ID, identity.ID); err != nil { + r.logger.ErrorCtx(ctx, "cannot provision member", log.Error(err)) + return nil, gqlutils.Internal(ctx) } w := gqlutils.HTTPResponseWriterFromContext(ctx) diff --git a/pkg/trust/service.go b/pkg/trust/service.go index 6749dcbd8..961f47b31 100644 --- a/pkg/trust/service.go +++ b/pkg/trust/service.go @@ -18,6 +18,7 @@ import ( "context" "errors" "fmt" + "time" "github.com/aws/aws-sdk-go-v2/service/s3" "go.gearno.de/kit/log" @@ -337,3 +338,106 @@ func (s *Service) GetNDAFile( return file, nil } + +func (s *Service) ProvisionMember( + ctx context.Context, + compliancePageID gid.GID, + identityID gid.GID, +) (*coredata.TrustCenterAccess, error) { + var ( + access *coredata.TrustCenterAccess + now = time.Now() + scope = coredata.NewScopeFromObjectID(compliancePageID) + ) + + err := s.pg.WithTx( + ctx, + func(tx pg.Conn) error { + compliancePage := &coredata.TrustCenter{} + if err := compliancePage.LoadByID(ctx, tx, scope, compliancePageID); err != nil { + return fmt.Errorf("cannot load trust center: %w", err) + } + + identity := &coredata.Identity{} + if err := identity.LoadByID(ctx, tx, identityID); err != nil { + return fmt.Errorf("cannot load identity: %w", err) + } + + access := &coredata.TrustCenterAccess{} + if err := access.LoadByTrustCenterIDAndIdentityID(ctx, tx, scope, compliancePageID, identityID); err != nil { + if !errors.Is(err, coredata.ErrResourceNotFound) { + return fmt.Errorf("cannot load trust center access: %w", err) + } + + access = &coredata.TrustCenterAccess{ + ID: gid.New(scope.GetTenantID(), coredata.TrustCenterAccessEntityType), + OrganizationID: compliancePage.OrganizationID, + TenantID: scope.GetTenantID(), + IdentityID: identityID, + TrustCenterID: compliancePageID, + CreatedAt: now, + UpdatedAt: now, + } + + var sig *coredata.ElectronicSignature + if compliancePage.NonDisclosureAgreementFileID != nil && s.esign != nil { + var err error + sig, err = s.esign.CreateSignature( + ctx, + tx, + &esign.CreateSignatureRequest{ + OrganizationID: access.OrganizationID, + DocumentType: coredata.ElectronicSignatureDocumentTypeNDA, + FileID: *compliancePage.NonDisclosureAgreementFileID, + SignerEmail: identity.EmailAddress, + }, + ) + if err != nil { + return fmt.Errorf("cannot create pending signature: %w", err) + } + } + + if sig != nil { + access.ElectronicSignatureID = &sig.ID + } + + if err := access.Insert(ctx, tx, scope); err != nil { + return fmt.Errorf("cannot insert trust center access: %w", err) + } + } + + profile := &coredata.MembershipProfile{} + if err := profile.LoadByIdentityIDAndOrganizationID( + ctx, + tx, + coredata.NewScopeFromObjectID(access.ID), + identityID, + access.OrganizationID, + ); err != nil { + if !errors.Is(err, coredata.ErrResourceNotFound) { + return fmt.Errorf("cannot load profile: %w", err) + } + + profile = &coredata.MembershipProfile{ + ID: gid.New(access.TenantID, coredata.MembershipProfileEntityType), + IdentityID: identityID, + OrganizationID: access.OrganizationID, + EmailAddress: identity.EmailAddress, + Source: coredata.ProfileSourceManual, + State: coredata.ProfileStateActive, + FullName: identity.FullName, + CreatedAt: now, + UpdatedAt: now, + } + + if err := profile.Insert(ctx, tx); err != nil { + return fmt.Errorf("cannot insert profile: %w", err) + } + } + + return nil + }, + ) + + return access, err +} diff --git a/pkg/trust/trust_center_access_service.go b/pkg/trust/trust_center_access_service.go index 35148f8d5..7ed38f284 100644 --- a/pkg/trust/trust_center_access_service.go +++ b/pkg/trust/trust_center_access_service.go @@ -24,7 +24,6 @@ import ( "go.gearno.de/kit/pg" "go.probo.inc/probo/packages/emails" "go.probo.inc/probo/pkg/coredata" - "go.probo.inc/probo/pkg/esign" "go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/iam" "go.probo.inc/probo/pkg/mail" @@ -50,126 +49,6 @@ const ( TrustCenterAccessURLFormat = "https://%s/organizations/%s/trust-center/access" ) -func (s TrustCenterAccessService) ensureAccessInTx( - ctx context.Context, - tx pg.Conn, - trustCenterID gid.GID, - identityID gid.GID, -) (*coredata.TrustCenterAccess, *coredata.TrustCenter, error) { - now := time.Now() - - trustCenter := &coredata.TrustCenter{} - if err := trustCenter.LoadByID(ctx, tx, s.svc.scope, trustCenterID); err != nil { - return nil, nil, fmt.Errorf("cannot load trust center: %w", err) - } - - identity := &coredata.Identity{} - if err := identity.LoadByID(ctx, tx, identityID); err != nil { - return nil, nil, fmt.Errorf("cannot load identity: %w", err) - } - - existingAccess := &coredata.TrustCenterAccess{} - err := existingAccess.LoadByTrustCenterIDAndIdentityID(ctx, tx, s.svc.scope, trustCenterID, identityID) - if err == nil { - return existingAccess, trustCenter, nil - } - - if !errors.Is(err, coredata.ErrResourceNotFound) { - return nil, nil, fmt.Errorf("cannot load trust center access: %w", err) - } - - access := &coredata.TrustCenterAccess{ - ID: gid.New(s.svc.scope.GetTenantID(), coredata.TrustCenterAccessEntityType), - OrganizationID: trustCenter.OrganizationID, - TenantID: s.svc.scope.GetTenantID(), - IdentityID: identityID, - TrustCenterID: trustCenterID, - CreatedAt: now, - UpdatedAt: now, - } - - if trustCenter.NonDisclosureAgreementFileID != nil && s.svc.esign != nil { - sig, err := s.svc.esign.CreateSignature( - ctx, - tx, - &esign.CreateSignatureRequest{ - OrganizationID: access.OrganizationID, - DocumentType: coredata.ElectronicSignatureDocumentTypeNDA, - FileID: *trustCenter.NonDisclosureAgreementFileID, - SignerEmail: identity.EmailAddress, - }, - ) - if err != nil { - return nil, nil, fmt.Errorf("cannot create pending signature: %w", err) - } - access.ElectronicSignatureID = &sig.ID - } - - if err := access.Insert(ctx, tx, s.svc.scope); err != nil { - return nil, nil, fmt.Errorf("cannot insert trust center access: %w", err) - } - - return access, trustCenter, nil -} - -func (s TrustCenterAccessService) EnsureAccess( - ctx context.Context, - trustCenterID gid.GID, - identityID gid.GID, -) (*coredata.TrustCenterAccess, error) { - var access *coredata.TrustCenterAccess - - err := s.svc.pg.WithTx( - ctx, - func(tx pg.Conn) error { - now := time.Now() - - access, _, err := s.ensureAccessInTx(ctx, tx, trustCenterID, identityID) - if err != nil { - return fmt.Errorf("cannot ensure access presence: %w", err) - } - - identity := &coredata.Identity{} - if err := identity.LoadByID(ctx, tx, identityID); err != nil { - return fmt.Errorf("cannot load identity: %w", err) - } - - profile := &coredata.MembershipProfile{} - if err := profile.LoadByIdentityIDAndOrganizationID( - ctx, - tx, - coredata.NewScopeFromObjectID(access.ID), - identityID, - access.OrganizationID, - ); err != nil { - if !errors.Is(err, coredata.ErrResourceNotFound) { - return fmt.Errorf("cannot load profile: %w", err) - } - - profile = &coredata.MembershipProfile{ - ID: gid.New(access.TenantID, coredata.MembershipProfileEntityType), - IdentityID: identityID, - OrganizationID: access.OrganizationID, - EmailAddress: identity.EmailAddress, - Source: coredata.ProfileSourceManual, - State: coredata.ProfileStateActive, - FullName: identity.FullName, - CreatedAt: now, - UpdatedAt: now, - } - - if err := profile.Insert(ctx, tx); err != nil { - return fmt.Errorf("cannot insert profile: %w", err) - } - } - - return err - }, - ) - - return access, err -} - func (s TrustCenterAccessService) Request( ctx context.Context, req *TrustCenterAccessRequest,