Create access at login time
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
@@ -28,7 +28,7 @@ export function MainLayout(props: Props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const nda = trustCenter.nonDisclosureAgreement;
|
const nda = trustCenter.nonDisclosureAgreement;
|
||||||
const hasPendingNDA = nda
|
const hasPendingNDA = data.viewer && nda
|
||||||
&& (!nda.viewerSignature || nda.viewerSignature.status === "PENDING" || nda.viewerSignature.status === "FAILED");
|
&& (!nda.viewerSignature || nda.viewerSignature.status === "PENDING" || nda.viewerSignature.status === "FAILED");
|
||||||
|
|
||||||
if (hasPendingNDA) {
|
if (hasPendingNDA) {
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ const ndaPageFragment = graphql`
|
|||||||
fragment NDAPageFragment on TrustCenter
|
fragment NDAPageFragment on TrustCenter
|
||||||
@refetchable(queryName: "NDAPageRefetchQuery") {
|
@refetchable(queryName: "NDAPageRefetchQuery") {
|
||||||
nonDisclosureAgreement @required(action: THROW) {
|
nonDisclosureAgreement @required(action: THROW) {
|
||||||
viewerSignature @required(action: THROW) {
|
viewerSignature {
|
||||||
id
|
id
|
||||||
status
|
status
|
||||||
consentText
|
consentText
|
||||||
@@ -124,11 +124,11 @@ export function NDAPage(props: {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const isProcessing
|
const isProcessing
|
||||||
= ndaSignature.status === "ACCEPTED"
|
= ndaSignature?.status === "ACCEPTED"
|
||||||
|| ndaSignature.status === "PROCESSING";
|
|| ndaSignature?.status === "PROCESSING";
|
||||||
|
|
||||||
const isFailed = ndaSignature.status === "FAILED";
|
const isFailed = ndaSignature?.status === "FAILED";
|
||||||
const isCompleted = ndaSignature.status === "COMPLETED";
|
const isCompleted = ndaSignature?.status === "COMPLETED";
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isCompleted) {
|
if (isCompleted) {
|
||||||
@@ -200,11 +200,11 @@ export function NDAPage(props: {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const nda = trustCenter.nonDisclosureAgreement;
|
const nda = trustCenter.nonDisclosureAgreement;
|
||||||
if (
|
if (!viewer) {
|
||||||
!nda
|
return <Navigate to="/connect" replace />;
|
||||||
|| !nda.viewerSignature
|
}
|
||||||
|| nda.viewerSignature.status === "COMPLETED"
|
|
||||||
) {
|
if (!nda || !nda.viewerSignature || nda.viewerSignature.status === "COMPLETED") {
|
||||||
return <Navigate to="/overview" replace />;
|
return <Navigate to="/overview" replace />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Suspense, useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { useQueryLoader } from "react-relay";
|
import { useQueryLoader } from "react-relay";
|
||||||
|
|
||||||
import { RelayProvider } from "#/providers/RelayProviders";
|
import { RelayProvider } from "#/providers/RelayProviders";
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* @generated SignedSource<<99908411212decf464eafa45b1fc0e04>>
|
* @generated SignedSource<<e22127e4a2337413e7cbe8d64f43fa95>>
|
||||||
* @lightSyntaxTransform
|
* @lightSyntaxTransform
|
||||||
* @nogrep
|
* @nogrep
|
||||||
*/
|
*/
|
||||||
@@ -19,7 +19,7 @@ export type NDAPageFragment$data = {
|
|||||||
readonly id: string;
|
readonly id: string;
|
||||||
readonly lastError: string | null | undefined;
|
readonly lastError: string | null | undefined;
|
||||||
readonly status: ElectronicSignatureStatus;
|
readonly status: ElectronicSignatureStatus;
|
||||||
};
|
} | null | undefined;
|
||||||
};
|
};
|
||||||
readonly " $fragmentType": "NDAPageFragment";
|
readonly " $fragmentType": "NDAPageFragment";
|
||||||
};
|
};
|
||||||
@@ -67,8 +67,6 @@ return {
|
|||||||
"plural": false,
|
"plural": false,
|
||||||
"selections": [
|
"selections": [
|
||||||
{
|
{
|
||||||
"kind": "RequiredField",
|
|
||||||
"field": {
|
|
||||||
"alias": null,
|
"alias": null,
|
||||||
"args": null,
|
"args": null,
|
||||||
"concreteType": "ElectronicSignature",
|
"concreteType": "ElectronicSignature",
|
||||||
@@ -100,8 +98,6 @@ return {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
|
||||||
"action": "THROW"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
@@ -115,6 +111,6 @@ return {
|
|||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
(node as any).hash = "503859b8c00b27214f9f3253bffcb670";
|
(node as any).hash = "37f6ca36229313e341797c28d789035d";
|
||||||
|
|
||||||
export default node;
|
export default node;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* @generated SignedSource<<5c6134b4d6615f24a63ea534c80e76c1>>
|
* @generated SignedSource<<2b8f94503fe145fb141255a7ba79ddac>>
|
||||||
* @lightSyntaxTransform
|
* @lightSyntaxTransform
|
||||||
* @nogrep
|
* @nogrep
|
||||||
*/
|
*/
|
||||||
@@ -161,6 +161,6 @@ return {
|
|||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
(node as any).hash = "503859b8c00b27214f9f3253bffcb670";
|
(node as any).hash = "37f6ca36229313e341797c28d789035d";
|
||||||
|
|
||||||
export default node;
|
export default node;
|
||||||
|
|||||||
@@ -174,7 +174,7 @@ func (r *mutationResolver) SendMagicLink(ctx context.Context, input types.SendMa
|
|||||||
|
|
||||||
// VerifyMagicLink is the resolver for the verifyMagicLink field.
|
// VerifyMagicLink is the resolver for the verifyMagicLink field.
|
||||||
func (r *mutationResolver) VerifyMagicLink(ctx context.Context, input types.VerifyMagicLinkInput) (*types.VerifyMagicLinkPayload, error) {
|
func (r *mutationResolver) VerifyMagicLink(ctx context.Context, input types.VerifyMagicLinkInput) (*types.VerifyMagicLinkPayload, error) {
|
||||||
_, session, err := r.iam.AuthService.OpenSessionWithMagicLink(ctx, input.Token)
|
identity, session, err := r.iam.AuthService.OpenSessionWithMagicLink(ctx, input.Token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var errInvalidToken *iam.ErrInvalidToken
|
var errInvalidToken *iam.ErrInvalidToken
|
||||||
if errors.As(err, &errInvalidToken) {
|
if errors.As(err, &errInvalidToken) {
|
||||||
@@ -185,6 +185,13 @@ func (r *mutationResolver) VerifyMagicLink(ctx context.Context, input types.Veri
|
|||||||
return nil, gqlutils.Internal(ctx)
|
return nil, gqlutils.Internal(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
r.logger.ErrorCtx(ctx, "cannot ensure trust center access", log.Error(err))
|
||||||
|
}
|
||||||
|
|
||||||
w := gqlutils.HTTPResponseWriterFromContext(ctx)
|
w := gqlutils.HTTPResponseWriterFromContext(ctx)
|
||||||
r.sessionCookie.Set(w, session)
|
r.sessionCookie.Set(w, session)
|
||||||
|
|
||||||
|
|||||||
@@ -60,6 +60,87 @@ func (tcar *TrustCenterAccessRequest) Validate() error {
|
|||||||
return v.Error()
|
return v.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s TrustCenterAccessService) ensureAccessInTx(
|
||||||
|
ctx context.Context,
|
||||||
|
tx pg.Conn,
|
||||||
|
trustCenterID gid.GID,
|
||||||
|
email mail.Addr,
|
||||||
|
fullName string,
|
||||||
|
) (*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)
|
||||||
|
}
|
||||||
|
|
||||||
|
existingAccess := &coredata.TrustCenterAccess{}
|
||||||
|
err := existingAccess.LoadByTrustCenterIDAndEmail(ctx, tx, s.svc.scope, trustCenterID, email)
|
||||||
|
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(),
|
||||||
|
TrustCenterID: trustCenterID,
|
||||||
|
Email: email,
|
||||||
|
Name: fullName,
|
||||||
|
State: coredata.TrustCenterAccessStateActive,
|
||||||
|
HasAcceptedNonDisclosureAgreement: false,
|
||||||
|
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: access.Email,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
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,
|
||||||
|
email mail.Addr,
|
||||||
|
fullName string,
|
||||||
|
) (*coredata.TrustCenterAccess, error) {
|
||||||
|
var access *coredata.TrustCenterAccess
|
||||||
|
|
||||||
|
err := s.svc.pg.WithTx(
|
||||||
|
ctx,
|
||||||
|
func(tx pg.Conn) error {
|
||||||
|
var err error
|
||||||
|
access, _, err = s.ensureAccessInTx(ctx, tx, trustCenterID, email, fullName)
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
return access, err
|
||||||
|
}
|
||||||
|
|
||||||
func (s TrustCenterAccessService) Request(
|
func (s TrustCenterAccessService) Request(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
req *TrustCenterAccessRequest,
|
req *TrustCenterAccessRequest,
|
||||||
@@ -71,18 +152,18 @@ func (s TrustCenterAccessService) Request(
|
|||||||
var (
|
var (
|
||||||
now = time.Now()
|
now = time.Now()
|
||||||
access *coredata.TrustCenterAccess
|
access *coredata.TrustCenterAccess
|
||||||
trustCenter *coredata.TrustCenter
|
|
||||||
)
|
)
|
||||||
|
|
||||||
err := s.svc.pg.WithTx(
|
err := s.svc.pg.WithTx(
|
||||||
ctx,
|
ctx,
|
||||||
func(tx pg.Conn) error {
|
func(tx pg.Conn) error {
|
||||||
trustCenter = &coredata.TrustCenter{}
|
var trustCenter *coredata.TrustCenter
|
||||||
if err := trustCenter.LoadByID(ctx, tx, s.svc.scope, req.TrustCenterID); err != nil {
|
var err error
|
||||||
return fmt.Errorf("cannot load trust center: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: load document to ensure they are requestable
|
access, trustCenter, err = s.ensureAccessInTx(ctx, tx, req.TrustCenterID, req.Email, req.FullName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
organizationID := trustCenter.OrganizationID
|
organizationID := trustCenter.OrganizationID
|
||||||
|
|
||||||
@@ -132,50 +213,6 @@ func (s TrustCenterAccessService) Request(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
existingAccess := &coredata.TrustCenterAccess{}
|
|
||||||
err := existingAccess.LoadByTrustCenterIDAndEmail(ctx, tx, s.svc.scope, req.TrustCenterID, req.Email)
|
|
||||||
if err == nil {
|
|
||||||
access = existingAccess
|
|
||||||
} else {
|
|
||||||
if !errors.Is(err, coredata.ErrResourceNotFound) {
|
|
||||||
return fmt.Errorf("cannot load trust center access: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
access = &coredata.TrustCenterAccess{
|
|
||||||
ID: gid.New(s.svc.scope.GetTenantID(), coredata.TrustCenterAccessEntityType),
|
|
||||||
OrganizationID: organizationID,
|
|
||||||
TenantID: s.svc.scope.GetTenantID(),
|
|
||||||
TrustCenterID: req.TrustCenterID,
|
|
||||||
Email: req.Email,
|
|
||||||
Name: req.FullName,
|
|
||||||
State: coredata.TrustCenterAccessStateActive,
|
|
||||||
HasAcceptedNonDisclosureAgreement: false,
|
|
||||||
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: access.Email,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("cannot create pending signature: %w", err)
|
|
||||||
}
|
|
||||||
access.ElectronicSignatureID = &sig.ID
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := access.Insert(ctx, tx, s.svc.scope); err != nil {
|
|
||||||
return fmt.Errorf("cannot insert trust center access: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var existingAccesses coredata.TrustCenterDocumentAccesses
|
var existingAccesses coredata.TrustCenterDocumentAccesses
|
||||||
if err := existingAccesses.LoadAllByTrustCenterAccessID(ctx, tx, s.svc.scope, access.ID); err != nil {
|
if err := existingAccesses.LoadAllByTrustCenterAccessID(ctx, tx, s.svc.scope, access.ID); err != nil {
|
||||||
return fmt.Errorf("cannot load existing access records: %w", err)
|
return fmt.Errorf("cannot load existing access records: %w", err)
|
||||||
|
|||||||
Reference in New Issue
Block a user