From 323cd602a11d08f4430d463a8beb10a05efdb754 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20R=C3=A9?= Date: Wed, 4 Mar 2026 18:47:12 +0400 Subject: [PATCH] Update business logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Émile Ré --- pkg/coredata/trust_center_access.go | 85 +++------ pkg/probo/trust_center_access_service.go | 90 ++++++---- .../compliancepage/membership_middleware.go | 75 -------- .../console/v1/types/trust_center_access.go | 10 +- pkg/server/api/console/v1/v1_resolver.go | 5 +- pkg/server/api/trust/v1/graphql_handler.go | 2 +- pkg/server/api/trust/v1/nda_directive.go | 20 ++- pkg/server/api/trust/v1/resolver.go | 1 - pkg/server/api/trust/v1/v1_resolver.go | 89 +++++----- pkg/slack/slack_message_service.go | 32 +++- pkg/trust/errors.go | 3 +- pkg/trust/service.go | 7 +- pkg/trust/trust_center_access_service.go | 165 ++++++++++-------- 13 files changed, 264 insertions(+), 320 deletions(-) delete mode 100644 pkg/server/api/compliancepage/membership_middleware.go diff --git a/pkg/coredata/trust_center_access.go b/pkg/coredata/trust_center_access.go index 515ef02ec..785acc5f3 100644 --- a/pkg/coredata/trust_center_access.go +++ b/pkg/coredata/trust_center_access.go @@ -16,7 +16,6 @@ package coredata import ( "context" - "encoding/json" "errors" "fmt" "maps" @@ -26,25 +25,20 @@ import ( "github.com/jackc/pgx/v5/pgconn" "go.gearno.de/kit/pg" "go.probo.inc/probo/pkg/gid" - "go.probo.inc/probo/pkg/mail" "go.probo.inc/probo/pkg/page" ) type ( TrustCenterAccess struct { - ID gid.GID `db:"id"` - OrganizationID gid.GID `db:"organization_id"` - TenantID gid.TenantID `db:"tenant_id"` - TrustCenterID gid.GID `db:"trust_center_id"` - Email mail.Addr `db:"email"` - Name string `db:"name"` - State TrustCenterAccessState `db:"state"` - HasAcceptedNonDisclosureAgreement bool `db:"has_accepted_non_disclosure_agreement"` - HasAcceptedNonDisclosureAgreementMetadata json.RawMessage `db:"has_accepted_non_disclosure_agreement_metadata"` - NDAFileID *gid.GID `db:"nda_file_id"` - ElectronicSignatureID *gid.GID `db:"electronic_signature_id"` - CreatedAt time.Time `db:"created_at"` - UpdatedAt time.Time `db:"updated_at"` + ID gid.GID `db:"id"` + OrganizationID gid.GID `db:"organization_id"` + TenantID gid.TenantID `db:"tenant_id"` + IdentityID gid.GID `db:"identity_id"` + TrustCenterID gid.GID `db:"trust_center_id"` + NDAFileID *gid.GID `db:"nda_file_id"` + ElectronicSignatureID *gid.GID `db:"electronic_signature_id"` + CreatedAt time.Time `db:"created_at"` + UpdatedAt time.Time `db:"updated_at"` } TrustCenterAccesses []*TrustCenterAccess @@ -85,11 +79,6 @@ SELECT organization_id, tenant_id, trust_center_id, - email, - name, - state, - has_accepted_non_disclosure_agreement, - has_accepted_non_disclosure_agreement_metadata, nda_file_id, electronic_signature_id, created_at, @@ -126,12 +115,12 @@ LIMIT 1; return nil } -func (tca *TrustCenterAccess) LoadByTrustCenterIDAndEmail( +func (tca *TrustCenterAccess) LoadByTrustCenterIDAndIdentityID( ctx context.Context, conn pg.Conn, scope Scoper, trustCenterID gid.GID, - email mail.Addr, + identityID gid.GID, ) error { q := ` SELECT @@ -139,11 +128,6 @@ SELECT organization_id, tenant_id, trust_center_id, - email, - name, - state, - has_accepted_non_disclosure_agreement, - has_accepted_non_disclosure_agreement_metadata, nda_file_id, electronic_signature_id, created_at, @@ -153,7 +137,7 @@ FROM WHERE %s AND trust_center_id = @trust_center_id - AND email = @email + AND identity_id = @identity_id LIMIT 1; ` @@ -161,7 +145,7 @@ LIMIT 1; args := pgx.StrictNamedArgs{ "trust_center_id": trustCenterID, - "email": email, + "identity_id": identityID, } maps.Copy(args, scope.SQLArguments()) @@ -195,10 +179,6 @@ INSERT INTO trust_center_accesses ( tenant_id, organization_id, trust_center_id, - email, - name, - state, - has_accepted_non_disclosure_agreement, electronic_signature_id, created_at, updated_at @@ -207,10 +187,6 @@ INSERT INTO trust_center_accesses ( @tenant_id, @organization_id, @trust_center_id, - @email, - @name, - @state, - @has_accepted_non_disclosure_agreement, @electronic_signature_id, @created_at, @updated_at @@ -218,17 +194,13 @@ INSERT INTO trust_center_accesses ( ` args := pgx.StrictNamedArgs{ - "id": tca.ID, - "tenant_id": tca.TenantID, - "organization_id": tca.OrganizationID, - "trust_center_id": tca.TrustCenterID, - "email": tca.Email, - "name": tca.Name, - "state": tca.State, - "has_accepted_non_disclosure_agreement": tca.HasAcceptedNonDisclosureAgreement, - "electronic_signature_id": tca.ElectronicSignatureID, - "created_at": tca.CreatedAt, - "updated_at": tca.UpdatedAt, + "id": tca.ID, + "tenant_id": tca.TenantID, + "organization_id": tca.OrganizationID, + "trust_center_id": tca.TrustCenterID, + "electronic_signature_id": tca.ElectronicSignatureID, + "created_at": tca.CreatedAt, + "updated_at": tca.UpdatedAt, } _, err := conn.Exec(ctx, q, args) @@ -252,11 +224,7 @@ func (tca *TrustCenterAccess) Update( ) error { q := ` UPDATE trust_center_accesses SET - name = @name, - state = @state, updated_at = @updated_at, - has_accepted_non_disclosure_agreement = @has_accepted_non_disclosure_agreement, - has_accepted_non_disclosure_agreement_metadata = @has_accepted_non_disclosure_agreement_metadata, nda_file_id = @nda_file_id, electronic_signature_id = @electronic_signature_id WHERE @@ -267,12 +235,8 @@ WHERE q = fmt.Sprintf(q, scope.SQLFragment()) args := pgx.StrictNamedArgs{ - "id": tca.ID, - "name": tca.Name, - "state": tca.State, - "updated_at": tca.UpdatedAt, - "has_accepted_non_disclosure_agreement": tca.HasAcceptedNonDisclosureAgreement, - "has_accepted_non_disclosure_agreement_metadata": tca.HasAcceptedNonDisclosureAgreementMetadata, + "id": tca.ID, + "updated_at": tca.UpdatedAt, "nda_file_id": tca.NDAFileID, "electronic_signature_id": tca.ElectronicSignatureID, } @@ -326,11 +290,6 @@ SELECT organization_id, tenant_id, trust_center_id, - email, - name, - state, - has_accepted_non_disclosure_agreement, - has_accepted_non_disclosure_agreement_metadata, nda_file_id, electronic_signature_id, created_at, diff --git a/pkg/probo/trust_center_access_service.go b/pkg/probo/trust_center_access_service.go index 90b4f1504..adf847d5d 100644 --- a/pkg/probo/trust_center_access_service.go +++ b/pkg/probo/trust_center_access_service.go @@ -38,8 +38,7 @@ type ( CreateTrustCenterAccessRequest struct { TrustCenterID gid.GID - Email mail.Addr - FullName string + IdentityID gid.GID } UpdateTrustCenterDocumentAccessRequest struct { @@ -49,8 +48,6 @@ type ( UpdateTrustCenterAccessRequest struct { ID gid.GID - Name *string - State *coredata.TrustCenterAccessState DocumentAccesses []UpdateTrustCenterDocumentAccessRequest ReportAccesses []UpdateTrustCenterDocumentAccessRequest TrustCenterFileAccesses []UpdateTrustCenterDocumentAccessRequest @@ -66,9 +63,6 @@ func (ctcar *CreateTrustCenterAccessRequest) Validate() error { v := validator.New() v.Check(ctcar.TrustCenterID, "trust_center_id", validator.Required(), validator.GID(coredata.TrustCenterEntityType)) - v.Check(ctcar.Email, "email", validator.Required(), validator.NotEmpty()) - v.Check(ctcar.Email.Domain(), "email", validator.NotBlacklisted()) - v.Check(ctcar.FullName, "name", validator.SafeTextNoNewLine(TitleMaxLength)) return v.Error() } @@ -77,7 +71,6 @@ func (utcar *UpdateTrustCenterAccessRequest) Validate() error { v := validator.New() v.Check(utcar.ID, "id", validator.Required(), validator.GID(coredata.TrustCenterAccessEntityType)) - v.Check(utcar.Name, "name", validator.SafeTextNoNewLine(TitleMaxLength)) for i, docAccess := range utcar.DocumentAccesses { v.Check(docAccess.ID, fmt.Sprintf("documentAccesses[%d].ID", i), validator.Required(), validator.GID(coredata.DocumentEntityType)) } @@ -237,17 +230,25 @@ func (s TrustCenterAccessService) Create( return fmt.Errorf("cannot load trust center: %w", err) } + // FIXME: need to change UX + profile := &coredata.MembershipProfile{} + if err := profile.LoadByIdentityIDAndOrganizationID(ctx, tx, s.svc.scope, trustCenter.OrganizationID, req.IdentityID); err != nil { + if !errors.Is(err, coredata.ErrResourceNotFound) { + return fmt.Errorf("cannot load profile: %w", err) + } + + if err := profile.Insert(ctx, tx); err != nil { + return fmt.Errorf("cannot insert profile: %w", err) + } + } + access = &coredata.TrustCenterAccess{ - ID: gid.New(s.svc.scope.GetTenantID(), coredata.TrustCenterAccessEntityType), - OrganizationID: trustCenter.OrganizationID, - TenantID: s.svc.scope.GetTenantID(), - TrustCenterID: req.TrustCenterID, - Email: req.Email, - Name: req.FullName, - State: coredata.TrustCenterAccessStateActive, - HasAcceptedNonDisclosureAgreement: false, - CreatedAt: now, - UpdatedAt: now, + ID: gid.New(s.svc.scope.GetTenantID(), coredata.TrustCenterAccessEntityType), + OrganizationID: trustCenter.OrganizationID, + TenantID: s.svc.scope.GetTenantID(), + TrustCenterID: req.TrustCenterID, + CreatedAt: now, + UpdatedAt: now, } if trustCenter.NonDisclosureAgreementFileID != nil && s.svc.esign != nil { @@ -258,7 +259,7 @@ func (s TrustCenterAccessService) Create( OrganizationID: access.OrganizationID, DocumentType: coredata.ElectronicSignatureDocumentTypeNDA, FileID: *trustCenter.NonDisclosureAgreementFileID, - SignerEmail: access.Email, + SignerEmail: profile.EmailAddress, }, ) if err != nil { @@ -292,9 +293,13 @@ func (s TrustCenterAccessService) Update( } now := time.Now() - var access *coredata.TrustCenterAccess - var trustCenterAcessActivated bool - var shouldUpdateSlackMessage bool + var ( + access *coredata.TrustCenterAccess + profile *coredata.MembershipProfile + trustCenterAcessActivated bool + shouldUpdateSlackMessage bool + ) + err := s.svc.pg.WithTx( ctx, func(tx pg.Conn) error { @@ -304,19 +309,24 @@ func (s TrustCenterAccessService) Update( return fmt.Errorf("cannot load trust center access: %w", err) } - trustCenterAcessActivated = req.State != nil && *req.State == coredata.TrustCenterAccessStateActive && access.State != coredata.TrustCenterAccessStateActive - if req.Name != nil { - access.Name = *req.Name - } - if req.State != nil { - access.State = *req.State - } access.UpdatedAt = now if err := access.Update(ctx, tx, s.svc.scope); err != nil { return fmt.Errorf("cannot update trust center access: %w", err) } + // FIXME: need to change UX + profile = &coredata.MembershipProfile{} + if err := profile.LoadByIdentityIDAndOrganizationID(ctx, tx, s.svc.scope, access.OrganizationID, access.IdentityID); err != nil { + if !errors.Is(err, coredata.ErrResourceNotFound) { + return fmt.Errorf("cannot load profile: %w", err) + } + + if err := profile.Insert(ctx, tx); err != nil { + return fmt.Errorf("cannot insert profile: %w", err) + } + } + var tcdas coredata.TrustCenterDocumentAccesses if len(req.DocumentAccesses) > 0 { @@ -370,8 +380,7 @@ func (s TrustCenterAccessService) Update( shouldUpdateSlackMessage = trustCenterAcessActivated || len(req.DocumentAccesses) > 0 || len(req.ReportAccesses) > 0 || - len(req.TrustCenterFileAccesses) > 0 || - req.Name != nil + len(req.TrustCenterFileAccesses) > 0 return nil }, @@ -382,7 +391,7 @@ func (s TrustCenterAccessService) Update( } if shouldUpdateSlackMessage { - if err := s.svc.SlackMessages.QueueSlackNotification(ctx, access.Email, access.TrustCenterID); err != nil { + if err := s.svc.SlackMessages.QueueSlackNotification(ctx, profile.IdentityID, access.TrustCenterID); err != nil { if !errors.Is(err, slack.ErrNoSlackConnector) { return nil, fmt.Errorf("cannot queue slack notification: %w", err) } @@ -429,12 +438,23 @@ func (s TrustCenterAccessService) sendAccessEmail(ctx context.Context, tx pg.Con return fmt.Errorf("cannot update trust center access with expiration: %w", err) } + profile := &coredata.MembershipProfile{} + if err := profile.LoadByIdentityIDAndOrganizationID( + ctx, + tx, + s.svc.scope, + access.IdentityID, + access.OrganizationID, + ); err != nil { + return fmt.Errorf("cannot load profile: %w", err) + } + emailPresenterCfg, err := s.svc.TrustCenters.EmailPresenterConfig(ctx, access.TrustCenterID) if err != nil { return fmt.Errorf("cannot get compliance page email presenter config: %w", err) } - emailPresenter := emails.NewPresenterFromConfig(s.svc.fileManager, emailPresenterCfg, access.Name) + emailPresenter := emails.NewPresenterFromConfig(s.svc.fileManager, emailPresenterCfg, profile.FullName) subject, textBody, htmlBody, err := emailPresenter.RenderTrustCenterAccess(ctx, organization.Name) if err != nil { @@ -442,8 +462,8 @@ func (s TrustCenterAccessService) sendAccessEmail(ctx context.Context, tx pg.Con } accessEmail := coredata.NewEmail( - access.Name, - access.Email, + profile.FullName, + profile.EmailAddress, subject, textBody, htmlBody, diff --git a/pkg/server/api/compliancepage/membership_middleware.go b/pkg/server/api/compliancepage/membership_middleware.go deleted file mode 100644 index 64c3998a1..000000000 --- a/pkg/server/api/compliancepage/membership_middleware.go +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) 2025 Probo Inc . -// -// 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 compliancepage - -import ( - "context" - "errors" - "net/http" - - "github.com/99designs/gqlgen/graphql" - "github.com/vektah/gqlparser/v2/gqlerror" - "go.gearno.de/kit/httpserver" - "go.gearno.de/kit/log" - "go.probo.inc/probo/pkg/coredata" - "go.probo.inc/probo/pkg/server/api/authn" - "go.probo.inc/probo/pkg/server/gqlutils" - "go.probo.inc/probo/pkg/trust" -) - -func NewMembershipMiddleware(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) { - ctx := r.Context() - - identity := authn.IdentityFromContext(r.Context()) - if identity == nil { - next.ServeHTTP(w, r) - return - } - compliancePage := CompliancePageFromContext(ctx) - - membership, err := trustSvc.GetMembershipByCompliancePageIDAndEmail(ctx, compliancePage.ID, identity.EmailAddress) - if err != nil { - if errors.Is(err, trust.ErrMembershipNotFound) { - next.ServeHTTP(w, r) - return - } - - logger.ErrorCtx(ctx, "cannot get membership by page id and email", log.Error(err)) - httpserver.RenderJSON( - w, - http.StatusInternalServerError, - &graphql.Response{ - Errors: gqlerror.List{ - gqlutils.Internal(ctx), - }, - }, - ) - return - } - - if membership.State == coredata.TrustCenterAccessStateActive { - ctx = context.WithValue(ctx, complianceMembershipKey, membership) - next.ServeHTTP(w, r.WithContext(ctx)) - return - } - - next.ServeHTTP(w, r) - }, - ) - } -} diff --git a/pkg/server/api/console/v1/types/trust_center_access.go b/pkg/server/api/console/v1/types/trust_center_access.go index f383276cb..8ad9e1d95 100644 --- a/pkg/server/api/console/v1/types/trust_center_access.go +++ b/pkg/server/api/console/v1/types/trust_center_access.go @@ -45,12 +45,8 @@ func NewTrustCenterAccessEdge(tca *coredata.TrustCenterAccess, orderBy coredata. func NewTrustCenterAccess(tca *coredata.TrustCenterAccess) *TrustCenterAccess { return &TrustCenterAccess{ - ID: tca.ID, - Email: tca.Email, - Name: tca.Name, - State: tca.State, - HasAcceptedNonDisclosureAgreement: tca.HasAcceptedNonDisclosureAgreement, - CreatedAt: tca.CreatedAt, - UpdatedAt: tca.UpdatedAt, + ID: tca.ID, + CreatedAt: tca.CreatedAt, + UpdatedAt: tca.UpdatedAt, } } diff --git a/pkg/server/api/console/v1/v1_resolver.go b/pkg/server/api/console/v1/v1_resolver.go index 5983719d3..993f9ee78 100644 --- a/pkg/server/api/console/v1/v1_resolver.go +++ b/pkg/server/api/console/v1/v1_resolver.go @@ -1940,8 +1940,7 @@ func (r *mutationResolver) CreateTrustCenterAccess(ctx context.Context, input ty ctx, &probo.CreateTrustCenterAccessRequest{ TrustCenterID: input.TrustCenterID, - Email: identity.EmailAddress, - FullName: identity.FullName, + IdentityID: identity.ID, }, ) if err != nil { @@ -1991,8 +1990,6 @@ func (r *mutationResolver) UpdateTrustCenterAccess(ctx context.Context, input ty ctx, &probo.UpdateTrustCenterAccessRequest{ ID: input.ID, - Name: input.Name, - State: input.State, DocumentAccesses: documentAccesses, ReportAccesses: reportAccesses, TrustCenterFileAccesses: fileAccesses, diff --git a/pkg/server/api/trust/v1/graphql_handler.go b/pkg/server/api/trust/v1/graphql_handler.go index 2040f8c11..3afc66b0b 100644 --- a/pkg/server/api/trust/v1/graphql_handler.go +++ b/pkg/server/api/trust/v1/graphql_handler.go @@ -40,7 +40,7 @@ func NewGraphQLHandler(iamSvc *iam.Service, trustSvc *trust.Service, esignSvc *e sessionCookie: authn.NewCookie(&cookieConfig), }, Directives: schema.DirectiveRoot{ - Nda: newNDADirective(logger, esignSvc), + Nda: newNDADirective(logger, trustSvc, esignSvc), Session: session.Directive, }, } diff --git a/pkg/server/api/trust/v1/nda_directive.go b/pkg/server/api/trust/v1/nda_directive.go index daa2b0aa8..ecd57aa04 100644 --- a/pkg/server/api/trust/v1/nda_directive.go +++ b/pkg/server/api/trust/v1/nda_directive.go @@ -16,6 +16,7 @@ package trust_v1 import ( "context" + "errors" "github.com/99designs/gqlgen/graphql" "go.gearno.de/kit/log" @@ -24,10 +25,12 @@ import ( "go.probo.inc/probo/pkg/server/api/authn" "go.probo.inc/probo/pkg/server/api/compliancepage" "go.probo.inc/probo/pkg/server/gqlutils" + "go.probo.inc/probo/pkg/trust" ) func newNDADirective( logger *log.Logger, + trustSvc *trust.Service, esignSvc *esign.Service, ) func(ctx context.Context, obj any, next graphql.Resolver) (any, error) { return func(ctx context.Context, obj any, next graphql.Resolver) (any, error) { @@ -36,9 +39,20 @@ func newNDADirective( return next(ctx) } - membership := compliancepage.ComplianceMembershipFromContext(ctx) - if membership == nil { - return nil, gqlutils.Unauthenticatedf(ctx, "authentication needed") + compliancePage := compliancepage.CompliancePageFromContext(ctx) + if compliancePage == nil { + logger.ErrorCtx(ctx, "cannot get compliance page from context") + return nil, gqlutils.Internal(ctx) + } + + membership, err := trustSvc.GetMembershipByCompliancePageIDAndIdentityID(ctx, compliancePage.ID, identity.ID) + if err != nil { + if errors.Is(err, trust.ErrMembershipNotFound) { + return nil, gqlutils.Unauthenticatedf(ctx, "authentication needed") + } + + logger.ErrorCtx(ctx, "cannot get compliance page membership", log.Error(err)) + return nil, gqlutils.Internal(ctx) } if membership.ElectronicSignatureID == nil { diff --git a/pkg/server/api/trust/v1/resolver.go b/pkg/server/api/trust/v1/resolver.go index bb0b384cf..12bb8118b 100644 --- a/pkg/server/api/trust/v1/resolver.go +++ b/pkg/server/api/trust/v1/resolver.go @@ -85,7 +85,6 @@ func NewMux( r.Use(compliancepage.NewCompliancePagePresenceMiddleware()) r.Use(authn.NewSessionMiddleware(iamSvc, cookieConfig)) - r.Use(compliancepage.NewMembershipMiddleware(trustSvc, logger)) graphqlHandler := NewGraphQLHandler(iamSvc, trustSvc, esignSvc, logger, baseURL, cookieConfig) diff --git a/pkg/server/api/trust/v1/v1_resolver.go b/pkg/server/api/trust/v1/v1_resolver.go index 6ff505d9e..5aa116e35 100644 --- a/pkg/server/api/trust/v1/v1_resolver.go +++ b/pkg/server/api/trust/v1/v1_resolver.go @@ -107,17 +107,14 @@ func (r *documentResolver) IsUserAuthorized(ctx context.Context, obj *types.Docu documentAccess, err := trustService.TrustCenterAccesses.GetDocumentAccess( ctx, trustCenter.ID, - identity.EmailAddress, + identity.ID, obj.ID, ) if err != nil { - if errors.Is(err, trust.ErrMembershipNotFound) { - return false, nil - } - if errors.Is(err, trust.ErrMembershipInactive) { - return false, nil - } - if errors.Is(err, trust.ErrDocumentAccessNotFound) { + if errors.Is(err, trust.ErrMembershipNotFound) || + errors.Is(err, trust.ErrUserNotFound) || + errors.Is(err, trust.ErrUserInactive) || + errors.Is(err, trust.ErrDocumentAccessNotFound) { return false, nil } @@ -141,15 +138,17 @@ func (r *documentResolver) Access(ctx context.Context, obj *types.Document) (*ty access, err := trustService.TrustCenterAccesses.GetDocumentAccess( ctx, trustCenter.ID, - identity.EmailAddress, + identity.ID, obj.ID, ) if err != nil { - if errors.Is(err, trust.ErrMembershipNotFound) || errors.Is(err, trust.ErrDocumentAccessNotFound) { + if errors.Is(err, trust.ErrMembershipNotFound) || + errors.Is(err, trust.ErrUserNotFound) || + errors.Is(err, trust.ErrDocumentAccessNotFound) { return nil, nil } - if errors.Is(err, trust.ErrMembershipInactive) { + if errors.Is(err, trust.ErrUserInactive) { return nil, gqlutils.Forbidden(ctx, err) } @@ -262,7 +261,7 @@ 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.EmailAddress, identity.FullName); err != nil { + if _, err := trustService.TrustCenterAccesses.EnsureAccess(ctx, trustCenter.ID, identity.ID); err != nil { r.logger.ErrorCtx(ctx, "cannot ensure trust center access", log.Error(err)) } @@ -288,8 +287,7 @@ func (r *mutationResolver) RequestAllAccesses(ctx context.Context) (*types.Reque ctx, &trust.TrustCenterAccessRequest{ TrustCenterID: trustCenter.ID, - Email: identity.EmailAddress, - FullName: identity.FullName, + IdentityID: identity.ID, DocumentIDs: nil, ReportIDs: nil, }, @@ -302,8 +300,6 @@ func (r *mutationResolver) RequestAllAccesses(ctx context.Context) (*types.Reque return &types.RequestAccessesPayload{ TrustCenterAccess: &types.TrustCenterAccess{ ID: access.ID, - Email: access.Email, - Name: access.Name, CreatedAt: access.CreatedAt, UpdatedAt: access.UpdatedAt, }, @@ -341,7 +337,7 @@ func (r *mutationResolver) ExportDocumentPDF(ctx context.Context, input types.Ex documentAccess, err := trustService.TrustCenterAccesses.GetDocumentAccess( ctx, trustCenter.ID, - identity.EmailAddress, + identity.ID, input.DocumentID, ) if err != nil { @@ -395,7 +391,7 @@ func (r *mutationResolver) ExportReportPDF(ctx context.Context, input types.Expo reportAccess, err := trustService.TrustCenterAccesses.GetReportAccess( ctx, trustCenter.ID, - identity.EmailAddress, + identity.ID, input.ReportID, ) if err != nil { @@ -447,7 +443,7 @@ func (r *mutationResolver) ExportTrustCenterFile(ctx context.Context, input type fileAccess, err := trustService.TrustCenterAccesses.GetTrustCenterFileAccess(ctx, trustCenter.ID, - identity.EmailAddress, + identity.ID, input.TrustCenterFileID, ) if err != nil { @@ -495,8 +491,7 @@ func (r *mutationResolver) RequestDocumentAccess(ctx context.Context, input type ctx, &trust.TrustCenterAccessRequest{ TrustCenterID: trustCenter.ID, - Email: identity.EmailAddress, - FullName: identity.FullName, + IdentityID: identity.ID, DocumentIDs: []gid.GID{input.DocumentID}, ReportIDs: []gid.GID{}, TrustCenterFileIDs: []gid.GID{}, @@ -538,8 +533,7 @@ func (r *mutationResolver) RequestReportAccess(ctx context.Context, input types. ctx, &trust.TrustCenterAccessRequest{ TrustCenterID: trustCenter.ID, - Email: identity.EmailAddress, - FullName: identity.FullName, + IdentityID: identity.ID, DocumentIDs: []gid.GID{}, ReportIDs: []gid.GID{input.ReportID}, TrustCenterFileIDs: []gid.GID{}, @@ -581,8 +575,7 @@ func (r *mutationResolver) RequestTrustCenterFileAccess(ctx context.Context, inp ctx, &trust.TrustCenterAccessRequest{ TrustCenterID: trustCenter.ID, - Email: identity.EmailAddress, - FullName: identity.FullName, + IdentityID: identity.ID, DocumentIDs: []gid.GID{}, ReportIDs: []gid.GID{}, TrustCenterFileIDs: []gid.GID{input.TrustCenterFileID}, @@ -676,7 +669,7 @@ func (r *nonDisclosureAgreementResolver) FileURL(ctx context.Context, obj *types if identity := authn.IdentityFromContext(ctx); identity != nil && r.esign != nil { trustService := r.TrustService(ctx, trustCenter.ID.TenantID()) - access, err := trustService.TrustCenterAccesses.GetAccess(ctx, trustCenter.ID, identity.EmailAddress) + access, err := trustService.TrustCenterAccesses.GetAccess(ctx, trustCenter.ID, identity.ID) if err == nil && access.ElectronicSignatureID != nil { fileURL, err := r.esign.GenerateSignatureFileURL(ctx, *access.ElectronicSignatureID, 15*time.Minute) if err == nil { @@ -707,7 +700,7 @@ func (r *nonDisclosureAgreementResolver) ViewerSignature(ctx context.Context, ob trustCenter := compliancepage.CompliancePageFromContext(ctx) trustService := r.TrustService(ctx, trustCenter.ID.TenantID()) - access, err := trustService.TrustCenterAccesses.GetAccess(ctx, trustCenter.ID, identity.EmailAddress) + access, err := trustService.TrustCenterAccesses.GetAccess(ctx, trustCenter.ID, identity.ID) if err != nil { return nil, nil } @@ -868,17 +861,14 @@ func (r *reportResolver) IsUserAuthorized(ctx context.Context, obj *types.Report reportAccess, err := trustService.TrustCenterAccesses.GetReportAccess(ctx, trustCenter.ID, - identity.EmailAddress, + identity.ID, obj.ID, ) if err != nil { - if errors.Is(err, trust.ErrMembershipNotFound) { - return false, nil - } - if errors.Is(err, trust.ErrMembershipInactive) { - return false, nil - } - if errors.Is(err, trust.ErrDocumentAccessNotFound) { + if errors.Is(err, trust.ErrMembershipNotFound) || + errors.Is(err, trust.ErrUserNotFound) || + errors.Is(err, trust.ErrUserInactive) || + errors.Is(err, trust.ErrDocumentAccessNotFound) { return false, nil } @@ -902,15 +892,17 @@ func (r *reportResolver) Access(ctx context.Context, obj *types.Report) (*types. access, err := trustService.TrustCenterAccesses.GetReportAccess( ctx, trustCenter.ID, - identity.EmailAddress, + identity.ID, obj.ID, ) if err != nil { - if errors.Is(err, trust.ErrMembershipNotFound) || errors.Is(err, trust.ErrDocumentAccessNotFound) { + if errors.Is(err, trust.ErrMembershipNotFound) || + errors.Is(err, trust.ErrUserNotFound) || + errors.Is(err, trust.ErrDocumentAccessNotFound) { return nil, nil } - if errors.Is(err, trust.ErrMembershipInactive) { + if errors.Is(err, trust.ErrUserInactive) { return nil, gqlutils.Forbidden(ctx, err) } @@ -1107,17 +1099,14 @@ func (r *trustCenterFileResolver) IsUserAuthorized(ctx context.Context, obj *typ fileAccess, err := trustService.TrustCenterAccesses.GetTrustCenterFileAccess(ctx, trustCenter.ID, - identity.EmailAddress, + identity.ID, obj.ID, ) if err != nil { - if errors.Is(err, trust.ErrMembershipNotFound) { - return false, nil - } - if errors.Is(err, trust.ErrMembershipInactive) { - return false, nil - } - if errors.Is(err, trust.ErrDocumentAccessNotFound) { + if errors.Is(err, trust.ErrMembershipNotFound) || + errors.Is(err, trust.ErrUserNotFound) || + errors.Is(err, trust.ErrUserInactive) || + errors.Is(err, trust.ErrDocumentAccessNotFound) { return false, nil } @@ -1141,15 +1130,17 @@ func (r *trustCenterFileResolver) Access(ctx context.Context, obj *types.TrustCe access, err := trustService.TrustCenterAccesses.GetTrustCenterFileAccess( ctx, trustCenter.ID, - identity.EmailAddress, + identity.ID, obj.ID, ) if err != nil { - if errors.Is(err, trust.ErrMembershipNotFound) || errors.Is(err, trust.ErrDocumentAccessNotFound) { + if errors.Is(err, trust.ErrMembershipNotFound) || + errors.Is(err, trust.ErrUserNotFound) || + errors.Is(err, trust.ErrDocumentAccessNotFound) { return nil, nil } - if errors.Is(err, trust.ErrMembershipInactive) { + if errors.Is(err, trust.ErrUserInactive) { return nil, gqlutils.Forbidden(ctx, err) } diff --git a/pkg/slack/slack_message_service.go b/pkg/slack/slack_message_service.go index 1a12993ba..dfeceacba 100644 --- a/pkg/slack/slack_message_service.go +++ b/pkg/slack/slack_message_service.go @@ -118,8 +118,13 @@ func (s *SlackMessageService) UpdateSlackAccessMessage( return fmt.Errorf("cannot load trust center: %w", err) } + identity := &coredata.Identity{} + if err := identity.LoadByEmail(ctx, tx, requesterEmail); err != nil { + return fmt.Errorf("cannot load identity: %w", err) + } + var trustCenterAccess coredata.TrustCenterAccess - if err := trustCenterAccess.LoadByTrustCenterIDAndEmail(ctx, tx, s.svc.scope, trustCenter.ID, requesterEmail); err != nil { + if err := trustCenterAccess.LoadByTrustCenterIDAndIdentityID(ctx, tx, s.svc.scope, trustCenter.ID, identity.ID); err != nil { return fmt.Errorf("cannot load trust center access: %w", err) } @@ -132,7 +137,7 @@ func (s *SlackMessageService) UpdateSlackAccessMessage( updatedBody, err := s.buildAccessRequestMessage( newSlackMessageID, - trustCenterAccess.Name, + identity.FullName, requesterEmail, trustCenter.OrganizationID, documents, @@ -179,12 +184,21 @@ func (s *SlackMessageService) UpdateSlackAccessMessage( func (s *SlackMessageService) QueueSlackNotification( ctx context.Context, - requesterEmail mail.Addr, + identityID gid.GID, trustCenterID gid.GID, ) error { return s.svc.pg.WithTx(ctx, func(tx pg.Conn) error { - var trustCenterAccess coredata.TrustCenterAccess - if err := trustCenterAccess.LoadByTrustCenterIDAndEmail(ctx, tx, s.svc.scope, trustCenterID, requesterEmail); err != nil { + var ( + identity = &coredata.Identity{} + trustCenterAccess *coredata.TrustCenterAccess + ) + + if err := identity.LoadByID(ctx, tx, identityID); err != nil { + return fmt.Errorf("cannot load identity: %w", err) + } + + trustCenterAccess = &coredata.TrustCenterAccess{} + if err := trustCenterAccess.LoadByTrustCenterIDAndIdentityID(ctx, tx, s.svc.scope, trustCenterID, identityID); err != nil { return fmt.Errorf("cannot load trust center access: %w", err) } @@ -224,8 +238,8 @@ func (s *SlackMessageService) QueueSlackNotification( body, err := s.buildAccessRequestMessage( slackMessageID, - trustCenterAccess.Name, - requesterEmail, + identity.FullName, + identity.EmailAddress, trustCenter.OrganizationID, documents, reports, @@ -247,7 +261,7 @@ func (s *SlackMessageService) QueueSlackNotification( OrganizationID: trustCenter.OrganizationID, Type: coredata.SlackMessageTypeTrustCenterAccessRequest, Body: body, - RequesterEmail: &requesterEmail, + RequesterEmail: &identity.EmailAddress, Metadata: metadata.toMap(), CreatedAt: now, UpdatedAt: now, @@ -261,7 +275,7 @@ func (s *SlackMessageService) QueueSlackNotification( tx, s.svc.scope, trustCenter.OrganizationID, - requesterEmail, + identity.EmailAddress, coredata.SlackMessageTypeTrustCenterAccessRequest, sevenDaysAgo, ) diff --git a/pkg/trust/errors.go b/pkg/trust/errors.go index 135fb906d..93c2aa192 100644 --- a/pkg/trust/errors.go +++ b/pkg/trust/errors.go @@ -20,7 +20,8 @@ var ( ErrCustomDomainNotFound = errors.New("custom domain not found") ErrPageNotFound = errors.New("page not found") ErrMembershipNotFound = errors.New("membership not found") - ErrMembershipInactive = errors.New("membership inactive") + ErrUserNotFound = errors.New("user not found") + ErrUserInactive = errors.New("user inactive") ErrDocumentAccessNotFound = errors.New("document access not found") ErrNDAFileNotFound = errors.New("NDA file not found") ) diff --git a/pkg/trust/service.go b/pkg/trust/service.go index c48e9706f..233c1bc6f 100644 --- a/pkg/trust/service.go +++ b/pkg/trust/service.go @@ -30,7 +30,6 @@ import ( "go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/html2pdf" "go.probo.inc/probo/pkg/iam" - "go.probo.inc/probo/pkg/mail" "go.probo.inc/probo/pkg/probo" "go.probo.inc/probo/pkg/slack" ) @@ -277,18 +276,18 @@ func (s *Service) EmailPresenterConfigByOrganizationID(ctx context.Context, orgI return s.WithTenant(orgID.TenantID()).TrustCenters.EmailPresenterConfig(ctx, trustCenter.ID) } -func (s *Service) GetMembershipByCompliancePageIDAndEmail(ctx context.Context, compliancePageID gid.GID, email mail.Addr) (*coredata.TrustCenterAccess, error) { +func (s *Service) GetMembershipByCompliancePageIDAndIdentityID(ctx context.Context, compliancePageID gid.GID, identityID gid.GID) (*coredata.TrustCenterAccess, error) { membership := &coredata.TrustCenterAccess{} err := s.pg.WithConn( ctx, func(conn pg.Conn) error { - return membership.LoadByTrustCenterIDAndEmail( + return membership.LoadByTrustCenterIDAndIdentityID( ctx, conn, coredata.NewScopeFromObjectID(compliancePageID), compliancePageID, - email, + identityID, ) }, ) diff --git a/pkg/trust/trust_center_access_service.go b/pkg/trust/trust_center_access_service.go index 4d2d08cc9..fe0bc465a 100644 --- a/pkg/trust/trust_center_access_service.go +++ b/pkg/trust/trust_center_access_service.go @@ -28,7 +28,6 @@ import ( "go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/iam" "go.probo.inc/probo/pkg/mail" - "go.probo.inc/probo/pkg/validator" ) type ( @@ -40,8 +39,7 @@ type ( TrustCenterAccessRequest struct { TrustCenterID gid.GID - Email mail.Addr - FullName string + IdentityID gid.GID DocumentIDs []gid.GID ReportIDs []gid.GID TrustCenterFileIDs []gid.GID @@ -52,20 +50,11 @@ const ( TrustCenterAccessURLFormat = "https://%s/organizations/%s/trust-center/access" ) -func (tcar *TrustCenterAccessRequest) Validate() error { - v := validator.New() - - v.Check(tcar.Email.Domain(), "email", validator.NotBlacklisted()) - - return v.Error() -} - func (s TrustCenterAccessService) ensureAccessInTx( ctx context.Context, tx pg.Conn, trustCenterID gid.GID, - email mail.Addr, - fullName string, + identityID gid.GID, ) (*coredata.TrustCenterAccess, *coredata.TrustCenter, error) { now := time.Now() @@ -74,8 +63,13 @@ func (s TrustCenterAccessService) ensureAccessInTx( 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.LoadByTrustCenterIDAndEmail(ctx, tx, s.svc.scope, trustCenterID, email) + err := existingAccess.LoadByTrustCenterIDAndIdentityID(ctx, tx, s.svc.scope, trustCenterID, identityID) if err == nil { return existingAccess, trustCenter, nil } @@ -85,16 +79,12 @@ func (s TrustCenterAccessService) ensureAccessInTx( } access := &coredata.TrustCenterAccess{ - ID: gid.New(s.svc.scope.GetTenantID(), coredata.TrustCenterAccessEntityType), - OrganizationID: trustCenter.OrganizationID, - TenantID: s.svc.scope.GetTenantID(), - TrustCenterID: trustCenterID, - Email: email, - Name: fullName, - State: coredata.TrustCenterAccessStateActive, - HasAcceptedNonDisclosureAgreement: false, - CreatedAt: now, - UpdatedAt: now, + ID: gid.New(s.svc.scope.GetTenantID(), coredata.TrustCenterAccessEntityType), + OrganizationID: trustCenter.OrganizationID, + TenantID: s.svc.scope.GetTenantID(), + TrustCenterID: trustCenterID, + CreatedAt: now, + UpdatedAt: now, } if trustCenter.NonDisclosureAgreementFileID != nil && s.svc.esign != nil { @@ -105,7 +95,7 @@ func (s TrustCenterAccessService) ensureAccessInTx( OrganizationID: access.OrganizationID, DocumentType: coredata.ElectronicSignatureDocumentTypeNDA, FileID: *trustCenter.NonDisclosureAgreementFileID, - SignerEmail: access.Email, + SignerEmail: identity.EmailAddress, }, ) if err != nil { @@ -124,8 +114,7 @@ func (s TrustCenterAccessService) ensureAccessInTx( func (s TrustCenterAccessService) EnsureAccess( ctx context.Context, trustCenterID gid.GID, - email mail.Addr, - fullName string, + identityID gid.GID, ) (*coredata.TrustCenterAccess, error) { var access *coredata.TrustCenterAccess @@ -133,7 +122,7 @@ func (s TrustCenterAccessService) EnsureAccess( ctx, func(tx pg.Conn) error { var err error - access, _, err = s.ensureAccessInTx(ctx, tx, trustCenterID, email, fullName) + access, _, err = s.ensureAccessInTx(ctx, tx, trustCenterID, identityID) return err }, ) @@ -145,10 +134,6 @@ func (s TrustCenterAccessService) Request( ctx context.Context, req *TrustCenterAccessRequest, ) (*coredata.TrustCenterAccess, error) { - if err := req.Validate(); err != nil { - return nil, fmt.Errorf("invalid request arguments: %w", err) - } - var ( now = time.Now() access *coredata.TrustCenterAccess @@ -160,7 +145,7 @@ func (s TrustCenterAccessService) Request( var trustCenter *coredata.TrustCenter var err error - access, trustCenter, err = s.ensureAccessInTx(ctx, tx, req.TrustCenterID, req.Email, req.FullName) + access, trustCenter, err = s.ensureAccessInTx(ctx, tx, req.TrustCenterID, req.IdentityID) if err != nil { return err } @@ -272,7 +257,7 @@ func (s TrustCenterAccessService) Request( return nil, err } - if err := s.svc.SlackMessages.QueueSlackNotification(ctx, access.Email, req.TrustCenterID); err != nil { + if err := s.svc.SlackMessages.QueueSlackNotification(ctx, req.IdentityID, req.TrustCenterID); err != nil { s.logger.ErrorCtx(ctx, "cannot queue slack notification", log.Error(err)) } @@ -282,12 +267,12 @@ func (s TrustCenterAccessService) Request( func (s TrustCenterAccessService) GetAccess( ctx context.Context, trustCenterID gid.GID, - email mail.Addr, + identityID gid.GID, ) (coredata.TrustCenterAccess, error) { var access coredata.TrustCenterAccess err := s.svc.pg.WithConn(ctx, func(conn pg.Conn) error { - return access.LoadByTrustCenterIDAndEmail(ctx, conn, s.svc.scope, trustCenterID, email) + return access.LoadByTrustCenterIDAndIdentityID(ctx, conn, s.svc.scope, trustCenterID, identityID) }) return access, err @@ -296,14 +281,14 @@ func (s TrustCenterAccessService) GetAccess( func (s TrustCenterAccessService) GetDocumentAccess( ctx context.Context, trustCenterID gid.GID, - email mail.Addr, + identityID gid.GID, documentID gid.GID, ) (*coredata.TrustCenterDocumentAccess, error) { var documentAccess *coredata.TrustCenterDocumentAccess err := s.svc.pg.WithConn(ctx, func(conn pg.Conn) error { access := &coredata.TrustCenterAccess{} - err := access.LoadByTrustCenterIDAndEmail(ctx, conn, s.svc.scope, trustCenterID, email) + err := access.LoadByTrustCenterIDAndIdentityID(ctx, conn, s.svc.scope, trustCenterID, identityID) if err != nil { if errors.Is(err, coredata.ErrResourceNotFound) { return ErrMembershipNotFound @@ -312,8 +297,15 @@ func (s TrustCenterAccessService) GetDocumentAccess( return fmt.Errorf("cannot load trust center access: %w", err) } - if access.State != coredata.TrustCenterAccessStateActive { - return ErrMembershipInactive + profile := &coredata.MembershipProfile{} + if err := profile.LoadByIdentityIDAndOrganizationID(ctx, conn, s.svc.scope, identityID, access.OrganizationID); err != nil { + if errors.Is(err, coredata.ErrResourceNotFound) { + return ErrUserNotFound + } + } + + if profile.State != coredata.ProfileStateActive { + return ErrUserInactive } documentAccess = &coredata.TrustCenterDocumentAccess{} @@ -339,14 +331,14 @@ func (s TrustCenterAccessService) GetDocumentAccess( func (s TrustCenterAccessService) GetReportAccess( ctx context.Context, trustCenterID gid.GID, - email mail.Addr, + identityID gid.GID, reportID gid.GID, ) (*coredata.TrustCenterDocumentAccess, error) { var reportAccess *coredata.TrustCenterDocumentAccess err := s.svc.pg.WithConn(ctx, func(conn pg.Conn) error { access := &coredata.TrustCenterAccess{} - err := access.LoadByTrustCenterIDAndEmail(ctx, conn, s.svc.scope, trustCenterID, email) + err := access.LoadByTrustCenterIDAndIdentityID(ctx, conn, s.svc.scope, trustCenterID, identityID) if err != nil { if errors.Is(err, coredata.ErrResourceNotFound) { return ErrMembershipNotFound @@ -355,8 +347,15 @@ func (s TrustCenterAccessService) GetReportAccess( return fmt.Errorf("cannot load trust center access: %w", err) } - if access.State != coredata.TrustCenterAccessStateActive { - return ErrMembershipInactive + profile := &coredata.MembershipProfile{} + if err := profile.LoadByIdentityIDAndOrganizationID(ctx, conn, s.svc.scope, identityID, access.OrganizationID); err != nil { + if errors.Is(err, coredata.ErrResourceNotFound) { + return ErrUserNotFound + } + } + + if profile.State != coredata.ProfileStateActive { + return ErrUserInactive } reportAccess = &coredata.TrustCenterDocumentAccess{} @@ -382,14 +381,14 @@ func (s TrustCenterAccessService) GetReportAccess( func (s TrustCenterAccessService) GetTrustCenterFileAccess( ctx context.Context, trustCenterID gid.GID, - email mail.Addr, + identityID gid.GID, trustCenterFileID gid.GID, ) (*coredata.TrustCenterDocumentAccess, error) { var fileAccess *coredata.TrustCenterDocumentAccess err := s.svc.pg.WithConn(ctx, func(conn pg.Conn) error { access := &coredata.TrustCenterAccess{} - err := access.LoadByTrustCenterIDAndEmail(ctx, conn, s.svc.scope, trustCenterID, email) + err := access.LoadByTrustCenterIDAndIdentityID(ctx, conn, s.svc.scope, trustCenterID, identityID) if err != nil { if errors.Is(err, coredata.ErrResourceNotFound) { return ErrMembershipNotFound @@ -398,8 +397,15 @@ func (s TrustCenterAccessService) GetTrustCenterFileAccess( return fmt.Errorf("cannot load trust center access: %w", err) } - if access.State != coredata.TrustCenterAccessStateActive { - return ErrMembershipInactive + profile := &coredata.MembershipProfile{} + if err := profile.LoadByIdentityIDAndOrganizationID(ctx, conn, s.svc.scope, identityID, access.OrganizationID); err != nil { + if errors.Is(err, coredata.ErrResourceNotFound) { + return ErrUserNotFound + } + } + + if profile.State != coredata.ProfileStateActive { + return ErrUserInactive } fileAccess = &coredata.TrustCenterDocumentAccess{} @@ -436,12 +442,28 @@ func (s *TrustCenterAccessService) GrantByIDs( return fmt.Errorf("cannot load trust center: %w", err) } + identity := &coredata.Identity{} + if err := identity.LoadByEmail(ctx, tx, email); err != nil { + return fmt.Errorf("cannot load identity: %w", err) + } + access := &coredata.TrustCenterAccess{} - if err := access.LoadByTrustCenterIDAndEmail(ctx, tx, s.svc.scope, trustCenter.ID, email); err != nil { + if err := access.LoadByTrustCenterIDAndIdentityID(ctx, tx, s.svc.scope, trustCenter.ID, identity.ID); err != nil { return fmt.Errorf("cannot load trust center access: %w", err) } - shouldSendEmail := access.State != coredata.TrustCenterAccessStateActive + profile := &coredata.MembershipProfile{} + if err := profile.LoadByIdentityIDAndOrganizationID(ctx, tx, s.svc.scope, identity.ID, access.OrganizationID); err != nil { + if errors.Is(err, coredata.ErrResourceNotFound) { + return ErrUserNotFound + } + } + + if profile.State != coredata.ProfileStateActive { + return ErrUserInactive + } + + shouldSendEmail := profile.State != coredata.ProfileStateActive now := time.Now() if len(documentIDs) > 0 { @@ -461,13 +483,13 @@ func (s *TrustCenterAccessService) GrantByIDs( } if shouldSendEmail { - access.State = coredata.TrustCenterAccessStateActive - access.UpdatedAt = now - if err := access.Update(ctx, tx, s.svc.scope); err != nil { - return fmt.Errorf("cannot update trust center access: %w", err) + profile.State = coredata.ProfileStateActive + profile.UpdatedAt = now + if err := profile.Update(ctx, tx, s.svc.scope); err != nil { + return fmt.Errorf("cannot update profile: %w", err) } - if err := s.sendAccessEmail(ctx, tx, access); err != nil { + if err := s.sendAccessEmail(ctx, tx, access, profile); err != nil { return fmt.Errorf("cannot send access email: %w", err) } } @@ -476,7 +498,7 @@ func (s *TrustCenterAccessService) GrantByIDs( }) } -func (s *TrustCenterAccessService) sendAccessEmail(ctx context.Context, tx pg.Conn, access *coredata.TrustCenterAccess) error { +func (s *TrustCenterAccessService) sendAccessEmail(ctx context.Context, tx pg.Conn, access *coredata.TrustCenterAccess, profile *coredata.MembershipProfile) error { organization := &coredata.Organization{} if err := organization.LoadByID(ctx, tx, s.svc.scope, access.OrganizationID); err != nil { return fmt.Errorf("cannot load organization: %w", err) @@ -494,7 +516,7 @@ func (s *TrustCenterAccessService) sendAccessEmail(ctx context.Context, tx pg.Co return fmt.Errorf("cannot get compliance page email presenter config: %w", err) } - emailPresenter := emails.NewPresenterFromConfig(s.svc.fileManager, emailPresenterCfg, access.Name) + emailPresenter := emails.NewPresenterFromConfig(s.svc.fileManager, emailPresenterCfg, profile.FullName) subject, textBody, htmlBody, err := emailPresenter.RenderTrustCenterAccess(ctx, organization.Name) if err != nil { @@ -502,8 +524,8 @@ func (s *TrustCenterAccessService) sendAccessEmail(ctx context.Context, tx pg.Co } accessEmail := coredata.NewEmail( - access.Name, - access.Email, + profile.FullName, + profile.EmailAddress, subject, textBody, htmlBody, @@ -529,11 +551,21 @@ func (s *TrustCenterAccessService) RejectOrRevokeByIDs( return fmt.Errorf("cannot load trust center: %w", err) } + identity := &coredata.Identity{} + if err := identity.LoadByEmail(ctx, tx, email); err != nil { + return fmt.Errorf("cannot load identity: %w", err) + } + access := &coredata.TrustCenterAccess{} - if err := access.LoadByTrustCenterIDAndEmail(ctx, tx, s.svc.scope, trustCenter.ID, email); err != nil { + if err := access.LoadByTrustCenterIDAndIdentityID(ctx, tx, s.svc.scope, trustCenter.ID, identity.ID); err != nil { return fmt.Errorf("cannot load trust center access: %w", err) } + profile := &coredata.MembershipProfile{} + if err := profile.LoadByIdentityIDAndOrganizationID(ctx, tx, s.svc.scope, identity.ID, access.OrganizationID); err != nil { + return fmt.Errorf("cannot load profile: %w", err) + } + shouldSendEmail := false now := time.Now() @@ -557,7 +589,7 @@ func (s *TrustCenterAccessService) RejectOrRevokeByIDs( } if shouldSendEmail { - if err := s.sendDocumentAccessRejectedEmail(ctx, tx, access, documentIDs, reportIDs, fileIDs); err != nil { + if err := s.sendDocumentAccessRejectedEmail(ctx, tx, access, profile, documentIDs, reportIDs, fileIDs); err != nil { return fmt.Errorf("cannot send access email: %w", err) } } @@ -570,6 +602,7 @@ func (s *TrustCenterAccessService) sendDocumentAccessRejectedEmail( ctx context.Context, tx pg.Conn, access *coredata.TrustCenterAccess, + profile *coredata.MembershipProfile, documentIDs []gid.GID, reportIDs []gid.GID, fileIDs []gid.GID, @@ -613,11 +646,7 @@ func (s *TrustCenterAccessService) sendDocumentAccessRejectedEmail( return fmt.Errorf("cannot get compliance page email presenter config: %w", err) } - fullName := access.Name - if fullName == "" { - fullName = access.Email.Username() - } - emailPresenter := emails.NewPresenterFromConfig(s.svc.fileManager, emailPresenterCfg, fullName) + emailPresenter := emails.NewPresenterFromConfig(s.svc.fileManager, emailPresenterCfg, profile.FullName) subject, textBody, htmlBody, err := emailPresenter.RenderTrustCenterDocumentAccessRejected( ctx, @@ -629,8 +658,8 @@ func (s *TrustCenterAccessService) sendDocumentAccessRejectedEmail( } accessEmail := coredata.NewEmail( - access.Name, - access.Email, + profile.FullName, + profile.EmailAddress, subject, textBody, htmlBody,