fix: update reauth constraints and tests

This commit is contained in:
David Nguyen
2024-03-26 18:33:20 +08:00
parent b6c4cc9dc8
commit c0fb5caf9c
13 changed files with 141 additions and 134 deletions

View File

@ -9,12 +9,12 @@ import { DateTime } from 'luxon';
import { signOut } from 'next-auth/react'; import { signOut } from 'next-auth/react';
import { match } from 'ts-pattern'; import { match } from 'ts-pattern';
import { RECIPIENT_ROLES_DESCRIPTION } from '@documenso/lib/constants/recipient-roles';
import { import {
DocumentAuth, DocumentAuth,
type TRecipientActionAuth, type TRecipientActionAuth,
type TRecipientActionAuthTypes, type TRecipientActionAuthTypes,
} from '@documenso/lib/types/document-auth'; } from '@documenso/lib/types/document-auth';
import type { FieldType } from '@documenso/prisma/client';
import { trpc } from '@documenso/trpc/react'; import { trpc } from '@documenso/trpc/react';
import { Alert, AlertDescription } from '@documenso/ui/primitives/alert'; import { Alert, AlertDescription } from '@documenso/ui/primitives/alert';
import { Button } from '@documenso/ui/primitives/button'; import { Button } from '@documenso/ui/primitives/button';
@ -33,7 +33,7 @@ export type DocumentActionAuthDialogProps = {
title?: string; title?: string;
documentAuthType: TRecipientActionAuthTypes; documentAuthType: TRecipientActionAuthTypes;
description?: string; description?: string;
actionTarget?: 'FIELD' | 'DOCUMENT'; actionTarget: FieldType | 'DOCUMENT';
isSubmitting?: boolean; isSubmitting?: boolean;
open: boolean; open: boolean;
onOpenChange: (value: boolean) => void; onOpenChange: (value: boolean) => void;
@ -53,7 +53,6 @@ export const DocumentActionAuthDialog = ({
title, title,
description, description,
documentAuthType, documentAuthType,
actionTarget = 'FIELD',
// onReauthFormSubmit, // onReauthFormSubmit,
isSubmitting, isSubmitting,
open, open,
@ -135,19 +134,14 @@ export const DocumentActionAuthDialog = ({
// setFormErrorCode(null); // setFormErrorCode(null);
// }, [open, form]); // }, [open, form]);
const defaultRecipientActionVerb = RECIPIENT_ROLES_DESCRIPTION[recipient.role].actionVerb;
return ( return (
<Dialog open={open} onOpenChange={handleOnOpenChange}> <Dialog open={open} onOpenChange={handleOnOpenChange}>
<DialogContent> <DialogContent>
<DialogHeader> <DialogHeader>
<DialogTitle> <DialogTitle>{title || 'Sign field'}</DialogTitle>
{title || `${defaultRecipientActionVerb} ${actionTarget.toLowerCase()}`}
</DialogTitle>
<DialogDescription> <DialogDescription>
{description || {description || `Reauthentication is required to sign the field`}
`Reauthentication is required to ${defaultRecipientActionVerb.toLowerCase()} the ${actionTarget.toLowerCase()}`}
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
@ -156,8 +150,7 @@ export const DocumentActionAuthDialog = ({
<fieldset disabled={isSigningOut} className="space-y-4"> <fieldset disabled={isSigningOut} className="space-y-4">
<Alert> <Alert>
<AlertDescription> <AlertDescription>
To {defaultRecipientActionVerb.toLowerCase()} this {actionTarget.toLowerCase()}, To sign this field, you need to be logged in as <strong>{recipient.email}</strong>
you need to be logged in as <strong>{recipient.email}</strong>
</AlertDescription> </AlertDescription>
</Alert> </Alert>

View File

@ -13,7 +13,7 @@ import type {
} from '@documenso/lib/types/document-auth'; } from '@documenso/lib/types/document-auth';
import { DocumentAuth } from '@documenso/lib/types/document-auth'; import { DocumentAuth } from '@documenso/lib/types/document-auth';
import { extractDocumentAuthMethods } from '@documenso/lib/utils/document-auth'; import { extractDocumentAuthMethods } from '@documenso/lib/utils/document-auth';
import type { Document, Recipient, User } from '@documenso/prisma/client'; import { type Document, FieldType, type Recipient, type User } from '@documenso/prisma/client';
import type { DocumentActionAuthDialogProps } from './document-action-auth-dialog'; import type { DocumentActionAuthDialogProps } from './document-action-auth-dialog';
import { DocumentActionAuthDialog } from './document-action-auth-dialog'; import { DocumentActionAuthDialog } from './document-action-auth-dialog';
@ -106,7 +106,7 @@ export const DocumentAuthProvider = ({
const executeActionAuthProcedure = async (options: ExecuteActionAuthProcedureOptions) => { const executeActionAuthProcedure = async (options: ExecuteActionAuthProcedureOptions) => {
// Directly run callback if no auth required. // Directly run callback if no auth required.
if (!derivedRecipientActionAuth) { if (!derivedRecipientActionAuth || options.actionTarget !== FieldType.SIGNATURE) {
await options.onReauthFormSubmit(); await options.onReauthFormSubmit();
return; return;
} }

View File

@ -20,7 +20,6 @@ import { Input } from '@documenso/ui/primitives/input';
import { Label } from '@documenso/ui/primitives/label'; import { Label } from '@documenso/ui/primitives/label';
import { SignaturePad } from '@documenso/ui/primitives/signature-pad'; import { SignaturePad } from '@documenso/ui/primitives/signature-pad';
import { useRequiredDocumentAuthContext } from './document-auth-provider';
import { useRequiredSigningContext } from './provider'; import { useRequiredSigningContext } from './provider';
import { SignDialog } from './sign-dialog'; import { SignDialog } from './sign-dialog';
@ -37,7 +36,6 @@ export const SigningForm = ({ document, recipient, fields, redirectUrl }: Signin
const { data: session } = useSession(); const { data: session } = useSession();
const { fullName, signature, setFullName, setSignature } = useRequiredSigningContext(); const { fullName, signature, setFullName, setSignature } = useRequiredSigningContext();
const { executeActionAuthProcedure } = useRequiredDocumentAuthContext();
const [validateUninsertedFields, setValidateUninsertedFields] = useState(false); const [validateUninsertedFields, setValidateUninsertedFields] = useState(false);
@ -67,10 +65,13 @@ export const SigningForm = ({ document, recipient, fields, redirectUrl }: Signin
return; return;
} }
await executeActionAuthProcedure({ await completeDocument();
onReauthFormSubmit: completeDocument,
actionTarget: 'DOCUMENT', // Reauth is currently not required for completing the document.
}); // await executeActionAuthProcedure({
// onReauthFormSubmit: completeDocument,
// actionTarget: 'DOCUMENT',
// });
}; };
const completeDocument = async (authOptions?: TRecipientActionAuth) => { const completeDocument = async (authOptions?: TRecipientActionAuth) => {

View File

@ -8,7 +8,7 @@ import { Loader } from 'lucide-react';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error'; import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import type { TRecipientActionAuth } from '@documenso/lib/types/document-auth'; import type { TRecipientActionAuth } from '@documenso/lib/types/document-auth';
import type { Recipient } from '@documenso/prisma/client'; import { type Recipient } from '@documenso/prisma/client';
import type { FieldWithSignature } from '@documenso/prisma/types/field-with-signature'; import type { FieldWithSignature } from '@documenso/prisma/types/field-with-signature';
import { trpc } from '@documenso/trpc/react'; import { trpc } from '@documenso/trpc/react';
import { Button } from '@documenso/ui/primitives/button'; import { Button } from '@documenso/ui/primitives/button';
@ -69,6 +69,7 @@ export const NameField = ({ field, recipient }: NameFieldProps) => {
void executeActionAuthProcedure({ void executeActionAuthProcedure({
onReauthFormSubmit: async (authOptions) => await onSign(authOptions, localFullName), onReauthFormSubmit: async (authOptions) => await onSign(authOptions, localFullName),
actionTarget: field.type,
}); });
}; };

View File

@ -12,8 +12,6 @@ import {
import { truncateTitle } from '~/helpers/truncate-title'; import { truncateTitle } from '~/helpers/truncate-title';
import { useRequiredDocumentAuthContext } from './document-auth-provider';
export type SignDialogProps = { export type SignDialogProps = {
isSubmitting: boolean; isSubmitting: boolean;
document: Document; document: Document;
@ -31,27 +29,26 @@ export const SignDialog = ({
onSignatureComplete, onSignatureComplete,
role, role,
}: SignDialogProps) => { }: SignDialogProps) => {
const { executeActionAuthProcedure, isAuthRedirectRequired } = useRequiredDocumentAuthContext();
const [showDialog, setShowDialog] = useState(false); const [showDialog, setShowDialog] = useState(false);
const truncatedTitle = truncateTitle(document.title); const truncatedTitle = truncateTitle(document.title);
const isComplete = fields.every((field) => field.inserted); const isComplete = fields.every((field) => field.inserted);
const handleOpenChange = async (open: boolean) => { const handleOpenChange = (open: boolean) => {
if (isSubmitting || !isComplete) { if (isSubmitting || !isComplete) {
return; return;
} }
if (isAuthRedirectRequired) { // Reauth is currently not required for signing the document.
await executeActionAuthProcedure({ // if (isAuthRedirectRequired) {
actionTarget: 'DOCUMENT', // await executeActionAuthProcedure({
onReauthFormSubmit: () => { // actionTarget: 'DOCUMENT',
// Do nothing since the user should be redirected. // onReauthFormSubmit: () => {
}, // // Do nothing since the user should be redirected.
}); // },
// });
return; // return;
} // }
setShowDialog(open); setShowDialog(open);
}; };

View File

@ -8,7 +8,7 @@ import { Loader } from 'lucide-react';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error'; import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import type { TRecipientActionAuth } from '@documenso/lib/types/document-auth'; import type { TRecipientActionAuth } from '@documenso/lib/types/document-auth';
import type { Recipient } from '@documenso/prisma/client'; import { type Recipient } from '@documenso/prisma/client';
import type { FieldWithSignature } from '@documenso/prisma/types/field-with-signature'; import type { FieldWithSignature } from '@documenso/prisma/types/field-with-signature';
import { trpc } from '@documenso/trpc/react'; import { trpc } from '@documenso/trpc/react';
import { Button } from '@documenso/ui/primitives/button'; import { Button } from '@documenso/ui/primitives/button';
@ -89,6 +89,7 @@ export const SignatureField = ({ field, recipient }: SignatureFieldProps) => {
void executeActionAuthProcedure({ void executeActionAuthProcedure({
onReauthFormSubmit: async (authOptions) => await onSign(authOptions, localSignature), onReauthFormSubmit: async (authOptions) => await onSign(authOptions, localSignature),
actionTarget: field.type,
}); });
}; };

View File

@ -3,6 +3,7 @@
import React from 'react'; import React from 'react';
import { type TRecipientActionAuth } from '@documenso/lib/types/document-auth'; import { type TRecipientActionAuth } from '@documenso/lib/types/document-auth';
import { FieldType } from '@documenso/prisma/client';
import type { FieldWithSignature } from '@documenso/prisma/types/field-with-signature'; import type { FieldWithSignature } from '@documenso/prisma/types/field-with-signature';
import { FieldRootContainer } from '@documenso/ui/components/field/field'; import { FieldRootContainer } from '@documenso/ui/components/field/field';
import { Tooltip, TooltipContent, TooltipTrigger } from '@documenso/ui/primitives/tooltip'; import { Tooltip, TooltipContent, TooltipTrigger } from '@documenso/ui/primitives/tooltip';
@ -55,11 +56,24 @@ export const SigningFieldContainer = ({
return; return;
} }
// Bypass reauth for non signature fields.
if (field.type !== FieldType.SIGNATURE) {
const presignResult = await onPreSign?.();
if (presignResult === false) {
return;
}
await onSign();
return;
}
if (isAuthRedirectRequired) { if (isAuthRedirectRequired) {
await executeActionAuthProcedure({ await executeActionAuthProcedure({
onReauthFormSubmit: () => { onReauthFormSubmit: () => {
// Do nothing since the user should be redirected. // Do nothing since the user should be redirected.
}, },
actionTarget: field.type,
}); });
return; return;
@ -76,6 +90,7 @@ export const SigningFieldContainer = ({
await executeActionAuthProcedure({ await executeActionAuthProcedure({
onReauthFormSubmit: onSign, onReauthFormSubmit: onSign,
actionTarget: field.type,
}); });
}; };

View File

@ -8,7 +8,7 @@ import { Loader } from 'lucide-react';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error'; import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import type { TRecipientActionAuth } from '@documenso/lib/types/document-auth'; import type { TRecipientActionAuth } from '@documenso/lib/types/document-auth';
import type { Recipient } from '@documenso/prisma/client'; import { type Recipient } from '@documenso/prisma/client';
import type { FieldWithSignature } from '@documenso/prisma/types/field-with-signature'; import type { FieldWithSignature } from '@documenso/prisma/types/field-with-signature';
import { trpc } from '@documenso/trpc/react'; import { trpc } from '@documenso/trpc/react';
import { Button } from '@documenso/ui/primitives/button'; import { Button } from '@documenso/ui/primitives/button';
@ -61,6 +61,7 @@ export const TextField = ({ field, recipient }: TextFieldProps) => {
void executeActionAuthProcedure({ void executeActionAuthProcedure({
onReauthFormSubmit: async (authOptions) => await onSign(authOptions), onReauthFormSubmit: async (authOptions) => await onSign(authOptions),
actionTarget: field.type,
}); });
}; };

View File

@ -124,7 +124,8 @@ test('[DOCUMENT_AUTH]: should allow signing with valid global auth', async ({ pa
await unseedUser(recipientWithAccount.id); await unseedUser(recipientWithAccount.id);
}); });
test('[DOCUMENT_AUTH]: should deny signing document when required for global auth', async ({ // Currently document auth for signing/approving/viewing is not required.
test.skip('[DOCUMENT_AUTH]: should deny signing document when required for global auth', async ({
page, page,
}) => { }) => {
const user = await seedUser(); const user = await seedUser();
@ -184,6 +185,10 @@ test('[DOCUMENT_AUTH]: should deny signing fields when required for global auth'
await expect(page.getByRole('heading', { name: 'Sign Document' })).toBeVisible(); await expect(page.getByRole('heading', { name: 'Sign Document' })).toBeVisible();
for (const field of Field) { for (const field of Field) {
if (field.type !== FieldType.SIGNATURE) {
continue;
}
await page.locator(`#field-${field.id}`).getByRole('button').click(); await page.locator(`#field-${field.id}`).getByRole('button').click();
await expect(page.getByRole('paragraph')).toContainText( await expect(page.getByRole('paragraph')).toContainText(
'Reauthentication is required to sign the field', 'Reauthentication is required to sign the field',
@ -249,6 +254,10 @@ test('[DOCUMENT_AUTH]: should allow field signing when required for recipient au
if (isAuthRequired) { if (isAuthRequired) {
for (const field of Field) { for (const field of Field) {
if (field.type !== FieldType.SIGNATURE) {
continue;
}
await page.locator(`#field-${field.id}`).getByRole('button').click(); await page.locator(`#field-${field.id}`).getByRole('button').click();
await expect(page.getByRole('paragraph')).toContainText( await expect(page.getByRole('paragraph')).toContainText(
'Reauthentication is required to sign the field', 'Reauthentication is required to sign the field',
@ -356,6 +365,10 @@ test('[DOCUMENT_AUTH]: should allow field signing when required for recipient an
if (isAuthRequired) { if (isAuthRequired) {
for (const field of Field) { for (const field of Field) {
if (field.type !== FieldType.SIGNATURE) {
continue;
}
await page.locator(`#field-${field.id}`).getByRole('button').click(); await page.locator(`#field-${field.id}`).getByRole('button').click();
await expect(page.getByRole('paragraph')).toContainText( await expect(page.getByRole('paragraph')).toContainText(
'Reauthentication is required to sign the field', 'Reauthentication is required to sign the field',

View File

@ -4,17 +4,21 @@ import path from 'node:path';
import { getDocumentByToken } from '@documenso/lib/server-only/document/get-document-by-token'; import { getDocumentByToken } from '@documenso/lib/server-only/document/get-document-by-token';
import { getRecipientByEmail } from '@documenso/lib/server-only/recipient/get-recipient-by-email'; import { getRecipientByEmail } from '@documenso/lib/server-only/recipient/get-recipient-by-email';
import { DocumentStatus } from '@documenso/prisma/client'; import { DocumentStatus } from '@documenso/prisma/client';
import { TEST_USER } from '@documenso/prisma/seed/pr-718-add-stepper-component'; import { seedUser } from '@documenso/prisma/seed/users';
import { apiSignin } from './fixtures/authentication';
test(`[PR-718]: should be able to create a document`, async ({ page }) => { test(`[PR-718]: should be able to create a document`, async ({ page }) => {
await page.goto('/signin'); await page.goto('/signin');
const documentTitle = `example-${Date.now()}.pdf`; const documentTitle = `example-${Date.now()}.pdf`;
// Sign in const user = await seedUser();
await page.getByLabel('Email').fill(TEST_USER.email);
await page.getByLabel('Password', { exact: true }).fill(TEST_USER.password); await apiSignin({
await page.getByRole('button', { name: 'Sign In' }).click(); page,
email: user.email,
});
// Upload document // Upload document
const [fileChooser] = await Promise.all([ const [fileChooser] = await Promise.all([
@ -82,10 +86,12 @@ test('should be able to create a document with multiple recipients', async ({ pa
const documentTitle = `example-${Date.now()}.pdf`; const documentTitle = `example-${Date.now()}.pdf`;
// Sign in const user = await seedUser();
await page.getByLabel('Email').fill(TEST_USER.email);
await page.getByLabel('Password', { exact: true }).fill(TEST_USER.password); await apiSignin({
await page.getByRole('button', { name: 'Sign In' }).click(); page,
email: user.email,
});
// Upload document // Upload document
const [fileChooser] = await Promise.all([ const [fileChooser] = await Promise.all([
@ -103,7 +109,7 @@ test('should be able to create a document with multiple recipients', async ({ pa
await page.waitForURL(/\/documents\/\d+/); await page.waitForURL(/\/documents\/\d+/);
// Set title // Set title
await expect(page.getByRole('heading', { name: 'Add Title' })).toBeVisible(); await expect(page.getByRole('heading', { name: 'General' })).toBeVisible();
await page.getByLabel('Title').fill(documentTitle); await page.getByLabel('Title').fill(documentTitle);
@ -112,13 +118,12 @@ test('should be able to create a document with multiple recipients', async ({ pa
// Add signers // Add signers
await expect(page.getByRole('heading', { name: 'Add Signers' })).toBeVisible(); await expect(page.getByRole('heading', { name: 'Add Signers' })).toBeVisible();
await page.getByLabel('Email*').fill('user1@example.com'); // Add 2 signers.
await page.getByLabel('Name').fill('User 1'); await page.getByPlaceholder('Email').fill('user1@example.com');
await page.getByPlaceholder('Name').fill('User 1');
await page.getByRole('button', { name: 'Add Signer' }).click(); await page.getByRole('button', { name: 'Add Signer' }).click();
await page.getByRole('textbox', { name: 'Email', exact: true }).fill('user2@example.com');
await page.getByLabel('Email*').nth(1).fill('user2@example.com'); await page.getByRole('textbox', { name: 'Name', exact: true }).fill('User 2');
await page.getByLabel('Name').nth(1).fill('User 2');
await page.getByRole('button', { name: 'Continue' }).click(); await page.getByRole('button', { name: 'Continue' }).click();
@ -177,10 +182,12 @@ test('should be able to create, send and sign a document', async ({ page }) => {
const documentTitle = `example-${Date.now()}.pdf`; const documentTitle = `example-${Date.now()}.pdf`;
// Sign in const user = await seedUser();
await page.getByLabel('Email').fill(TEST_USER.email);
await page.getByLabel('Password', { exact: true }).fill(TEST_USER.password); await apiSignin({
await page.getByRole('button', { name: 'Sign In' }).click(); page,
email: user.email,
});
// Upload document // Upload document
const [fileChooser] = await Promise.all([ const [fileChooser] = await Promise.all([
@ -198,7 +205,7 @@ test('should be able to create, send and sign a document', async ({ page }) => {
await page.waitForURL(/\/documents\/\d+/); await page.waitForURL(/\/documents\/\d+/);
// Set title // Set title
await expect(page.getByRole('heading', { name: 'Add Title' })).toBeVisible(); await expect(page.getByRole('heading', { name: 'General' })).toBeVisible();
await page.getByLabel('Title').fill(documentTitle); await page.getByLabel('Title').fill(documentTitle);
@ -207,8 +214,8 @@ test('should be able to create, send and sign a document', async ({ page }) => {
// Add signers // Add signers
await expect(page.getByRole('heading', { name: 'Add Signers' })).toBeVisible(); await expect(page.getByRole('heading', { name: 'Add Signers' })).toBeVisible();
await page.getByLabel('Email*').fill('user1@example.com'); await page.getByPlaceholder('Email').fill('user1@example.com');
await page.getByLabel('Name').fill('User 1'); await page.getByPlaceholder('Name').fill('User 1');
await page.getByRole('button', { name: 'Continue' }).click(); await page.getByRole('button', { name: 'Continue' }).click();
@ -225,8 +232,9 @@ test('should be able to create, send and sign a document', async ({ page }) => {
// Assert document was created // Assert document was created
await expect(page.getByRole('link', { name: documentTitle })).toBeVisible(); await expect(page.getByRole('link', { name: documentTitle })).toBeVisible();
await page.getByRole('link', { name: documentTitle }).click(); await page.getByRole('link', { name: documentTitle }).click();
await page.waitForURL(/\/documents\/\d+/);
const url = await page.url().split('/'); const url = page.url().split('/');
const documentId = url[url.length - 1]; const documentId = url[url.length - 1];
const { token } = await getRecipientByEmail({ const { token } = await getRecipientByEmail({
@ -260,10 +268,12 @@ test('should be able to create, send with redirect url, sign a document and redi
const documentTitle = `example-${Date.now()}.pdf`; const documentTitle = `example-${Date.now()}.pdf`;
// Sign in const user = await seedUser();
await page.getByLabel('Email').fill(TEST_USER.email);
await page.getByLabel('Password', { exact: true }).fill(TEST_USER.password); await apiSignin({
await page.getByRole('button', { name: 'Sign In' }).click(); page,
email: user.email,
});
// Upload document // Upload document
const [fileChooser] = await Promise.all([ const [fileChooser] = await Promise.all([
@ -280,18 +290,19 @@ test('should be able to create, send with redirect url, sign a document and redi
// Wait to be redirected to the edit page // Wait to be redirected to the edit page
await page.waitForURL(/\/documents\/\d+/); await page.waitForURL(/\/documents\/\d+/);
// Set title // Set title & advanced redirect
await expect(page.getByRole('heading', { name: 'Add Title' })).toBeVisible(); await expect(page.getByRole('heading', { name: 'General' })).toBeVisible();
await page.getByLabel('Title').fill(documentTitle); await page.getByLabel('Title').fill(documentTitle);
await page.getByRole('button', { name: 'Advanced Options' }).click();
await page.getByLabel('Redirect URL').fill('https://documenso.com');
await page.getByRole('button', { name: 'Continue' }).click(); await page.getByRole('button', { name: 'Continue' }).click();
// Add signers // Add signers
await expect(page.getByRole('heading', { name: 'Add Signers' })).toBeVisible(); await expect(page.getByRole('heading', { name: 'Add Signers' })).toBeVisible();
await page.getByLabel('Email*').fill('user1@example.com'); await page.getByPlaceholder('Email').fill('user1@example.com');
await page.getByLabel('Name').fill('User 1'); await page.getByPlaceholder('Name').fill('User 1');
await page.getByRole('button', { name: 'Continue' }).click(); await page.getByRole('button', { name: 'Continue' }).click();
@ -299,11 +310,6 @@ test('should be able to create, send with redirect url, sign a document and redi
await expect(page.getByRole('heading', { name: 'Add Fields' })).toBeVisible(); await expect(page.getByRole('heading', { name: 'Add Fields' })).toBeVisible();
await page.getByRole('button', { name: 'Continue' }).click(); await page.getByRole('button', { name: 'Continue' }).click();
// Add subject and send
await expect(page.getByRole('heading', { name: 'Add Subject' })).toBeVisible();
await page.getByRole('button', { name: 'Advanced Options' }).click();
await page.getByLabel('Redirect URL').fill('https://documenso.com');
await page.getByRole('button', { name: 'Send' }).click(); await page.getByRole('button', { name: 'Send' }).click();
await page.waitForURL('/documents'); await page.waitForURL('/documents');
@ -311,8 +317,9 @@ test('should be able to create, send with redirect url, sign a document and redi
// Assert document was created // Assert document was created
await expect(page.getByRole('link', { name: documentTitle })).toBeVisible(); await expect(page.getByRole('link', { name: documentTitle })).toBeVisible();
await page.getByRole('link', { name: documentTitle }).click(); await page.getByRole('link', { name: documentTitle }).click();
await page.waitForURL(/\/documents\/\d+/);
const url = await page.url().split('/'); const url = page.url().split('/');
const documentId = url[url.length - 1]; const documentId = url[url.length - 1];
const { token } = await getRecipientByEmail({ const { token } = await getRecipientByEmail({

View File

@ -7,11 +7,8 @@ import { prisma } from '@documenso/prisma';
import { DocumentStatus, SigningStatus } from '@documenso/prisma/client'; import { DocumentStatus, SigningStatus } from '@documenso/prisma/client';
import { WebhookTriggerEvents } from '@documenso/prisma/client'; import { WebhookTriggerEvents } from '@documenso/prisma/client';
import { AppError, AppErrorCode } from '../../errors/app-error';
import type { TRecipientActionAuth } from '../../types/document-auth'; import type { TRecipientActionAuth } from '../../types/document-auth';
import { extractDocumentAuthMethods } from '../../utils/document-auth';
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook'; import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
import { isRecipientAuthorized } from './is-recipient-authorized';
import { sealDocument } from './seal-document'; import { sealDocument } from './seal-document';
import { sendPendingEmail } from './send-pending-email'; import { sendPendingEmail } from './send-pending-email';
@ -46,8 +43,6 @@ const getDocument = async ({ token, documentId }: CompleteDocumentWithTokenOptio
export const completeDocumentWithToken = async ({ export const completeDocumentWithToken = async ({
token, token,
documentId, documentId,
userId,
authOptions,
requestMetadata, requestMetadata,
}: CompleteDocumentWithTokenOptions) => { }: CompleteDocumentWithTokenOptions) => {
'use server'; 'use server';
@ -79,22 +74,24 @@ export const completeDocumentWithToken = async ({
throw new Error(`Recipient ${recipient.id} has unsigned fields`); throw new Error(`Recipient ${recipient.id} has unsigned fields`);
} }
const { derivedRecipientActionAuth } = extractDocumentAuthMethods({ // Document reauth for completing documents is currently not required.
documentAuth: document.authOptions,
recipientAuth: recipient.authOptions,
});
const isValid = await isRecipientAuthorized({ // const { derivedRecipientActionAuth } = extractDocumentAuthMethods({
type: 'ACTION', // documentAuth: document.authOptions,
document: document, // recipientAuth: recipient.authOptions,
recipient: recipient, // });
userId,
authOptions,
});
if (!isValid) { // const isValid = await isRecipientAuthorized({
throw new AppError(AppErrorCode.UNAUTHORIZED, 'Invalid authentication values'); // type: 'ACTION',
} // document: document,
// recipient: recipient,
// userId,
// authOptions,
// });
// if (!isValid) {
// throw new AppError(AppErrorCode.UNAUTHORIZED, 'Invalid authentication values');
// }
await prisma.$transaction(async (tx) => { await prisma.$transaction(async (tx) => {
await tx.recipient.update({ await tx.recipient.update({
@ -121,7 +118,7 @@ export const completeDocumentWithToken = async ({
recipientName: recipient.name, recipientName: recipient.name,
recipientId: recipient.id, recipientId: recipient.id,
recipientRole: recipient.role, recipientRole: recipient.role,
actionAuth: derivedRecipientActionAuth || undefined, // actionAuth: derivedRecipientActionAuth || undefined,
}, },
}), }),
}); });

View File

@ -79,18 +79,28 @@ export const signFieldWithToken = async ({
throw new Error(`Field ${fieldId} has no recipientId`); throw new Error(`Field ${fieldId} has no recipientId`);
} }
const { derivedRecipientActionAuth } = extractDocumentAuthMethods({ let { derivedRecipientActionAuth } = extractDocumentAuthMethods({
documentAuth: document.authOptions, documentAuth: document.authOptions,
recipientAuth: recipient.authOptions, recipientAuth: recipient.authOptions,
}); });
const isValid = await isRecipientAuthorized({ // Override all non-signature fields to not require any auth.
type: 'ACTION', if (field.type !== FieldType.SIGNATURE) {
document: document, derivedRecipientActionAuth = null;
recipient: recipient, }
userId,
authOptions, let isValid = true;
});
// Only require auth on signature fields for now.
if (field.type === FieldType.SIGNATURE) {
isValid = await isRecipientAuthorized({
type: 'ACTION',
document: document,
recipient: recipient,
userId,
authOptions,
});
}
if (!isValid) { if (!isValid) {
throw new AppError(AppErrorCode.UNAUTHORIZED, 'Invalid authentication values'); throw new AppError(AppErrorCode.UNAUTHORIZED, 'Invalid authentication values');

View File

@ -1,29 +0,0 @@
import { hashSync } from '@documenso/lib/server-only/auth/hash';
import { prisma } from '..';
//
// https://github.com/documenso/documenso/pull/713
//
const PULL_REQUEST_NUMBER = 718;
const EMAIL_DOMAIN = `pr-${PULL_REQUEST_NUMBER}.documenso.com`;
export const TEST_USER = {
name: 'User 1',
email: `user1@${EMAIL_DOMAIN}`,
password: 'Password123',
} as const;
export const seedDatabase = async () => {
await prisma.user.create({
data: {
name: TEST_USER.name,
email: TEST_USER.email,
password: hashSync(TEST_USER.password),
emailVerified: new Date(),
url: TEST_USER.email,
},
});
};