mirror of
https://github.com/documenso/documenso.git
synced 2025-11-13 08:13:56 +10:00
fix: mask recipient tokens for non-owners
This commit is contained in:
@ -6,6 +6,7 @@ import { useCopyToClipboard } from '@documenso/lib/client-only/hooks/use-copy-to
|
|||||||
import { getRecipientType } from '@documenso/lib/client-only/recipient-type';
|
import { getRecipientType } from '@documenso/lib/client-only/recipient-type';
|
||||||
import { recipientAbbreviation } from '@documenso/lib/utils/recipient-formatter';
|
import { recipientAbbreviation } from '@documenso/lib/utils/recipient-formatter';
|
||||||
import type { Recipient } from '@documenso/prisma/client';
|
import type { Recipient } from '@documenso/prisma/client';
|
||||||
|
import { cn } from '@documenso/ui/lib/utils';
|
||||||
import { useToast } from '@documenso/ui/primitives/use-toast';
|
import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||||
|
|
||||||
import { StackAvatar } from './stack-avatar';
|
import { StackAvatar } from './stack-avatar';
|
||||||
@ -19,6 +20,10 @@ export function AvatarWithRecipient({ recipient }: AvatarWithRecipientProps) {
|
|||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
|
||||||
const onRecipientClick = () => {
|
const onRecipientClick = () => {
|
||||||
|
if (!recipient.token) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
void copy(`${process.env.NEXT_PUBLIC_WEBAPP_URL}/sign/${recipient.token}`).then(() => {
|
void copy(`${process.env.NEXT_PUBLIC_WEBAPP_URL}/sign/${recipient.token}`).then(() => {
|
||||||
toast({
|
toast({
|
||||||
title: 'Copied to clipboard',
|
title: 'Copied to clipboard',
|
||||||
@ -28,19 +33,22 @@ export function AvatarWithRecipient({ recipient }: AvatarWithRecipientProps) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="my-1 flex cursor-pointer items-center gap-2" onClick={onRecipientClick}>
|
<div
|
||||||
|
className={cn('my-1 flex items-center gap-2', {
|
||||||
|
'cursor-pointer hover:underline': recipient.token,
|
||||||
|
})}
|
||||||
|
role={recipient.token ? 'button' : undefined}
|
||||||
|
title={recipient.token && 'Click to copy signing link for sending to recipient'}
|
||||||
|
onClick={onRecipientClick}
|
||||||
|
>
|
||||||
<StackAvatar
|
<StackAvatar
|
||||||
first={true}
|
first={true}
|
||||||
key={recipient.id}
|
key={recipient.id}
|
||||||
type={getRecipientType(recipient)}
|
type={getRecipientType(recipient)}
|
||||||
fallbackText={recipientAbbreviation(recipient)}
|
fallbackText={recipientAbbreviation(recipient)}
|
||||||
/>
|
/>
|
||||||
<span
|
|
||||||
className="text-muted-foreground text-sm hover:underline"
|
<span className="text-muted-foreground text-sm">{recipient.email}</span>
|
||||||
title="Click to copy signing link for sending to recipient"
|
|
||||||
>
|
|
||||||
{recipient.email}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { SigningStatus } from '@documenso/prisma/client';
|
|||||||
import { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status';
|
import { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status';
|
||||||
|
|
||||||
import type { FindResultSet } from '../../types/find-result-set';
|
import type { FindResultSet } from '../../types/find-result-set';
|
||||||
|
import { maskRecipientTokensForDocument } from '../../utils/mask-recipient-tokens-for-document';
|
||||||
|
|
||||||
export type FindDocumentsOptions = {
|
export type FindDocumentsOptions = {
|
||||||
userId: number;
|
userId: number;
|
||||||
@ -173,8 +174,15 @@ export const findDocuments = async ({
|
|||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const maskedData = data.map((document) =>
|
||||||
|
maskRecipientTokensForDocument({
|
||||||
|
document,
|
||||||
|
user,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
data,
|
data: maskedData,
|
||||||
count,
|
count,
|
||||||
currentPage: Math.max(page, 1),
|
currentPage: Math.max(page, 1),
|
||||||
perPage,
|
perPage,
|
||||||
|
|||||||
@ -5,14 +5,16 @@ import type { Prisma } from '@prisma/client';
|
|||||||
import { prisma } from '@documenso/prisma';
|
import { prisma } from '@documenso/prisma';
|
||||||
|
|
||||||
export type UpdateDocumentOptions = {
|
export type UpdateDocumentOptions = {
|
||||||
documentId: number;
|
|
||||||
data: Prisma.DocumentUpdateInput;
|
data: Prisma.DocumentUpdateInput;
|
||||||
|
userId: number;
|
||||||
|
documentId: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const updateDocument = async ({ documentId, data }: UpdateDocumentOptions) => {
|
export const updateDocument = async ({ documentId, userId, data }: UpdateDocumentOptions) => {
|
||||||
return await prisma.document.update({
|
return await prisma.document.update({
|
||||||
where: {
|
where: {
|
||||||
id: documentId,
|
id: documentId,
|
||||||
|
userId,
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
...data,
|
...data,
|
||||||
|
|||||||
38
packages/lib/utils/mask-recipient-tokens-for-document.ts
Normal file
38
packages/lib/utils/mask-recipient-tokens-for-document.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import type { User } from '@documenso/prisma/client';
|
||||||
|
import type { DocumentWithRecipients } from '@documenso/prisma/types/document-with-recipient';
|
||||||
|
|
||||||
|
export type MaskRecipientTokensForDocumentOptions<T extends DocumentWithRecipients> = {
|
||||||
|
document: T;
|
||||||
|
user?: User;
|
||||||
|
token?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const maskRecipientTokensForDocument = <T extends DocumentWithRecipients>({
|
||||||
|
document,
|
||||||
|
user,
|
||||||
|
token,
|
||||||
|
}: MaskRecipientTokensForDocumentOptions<T>) => {
|
||||||
|
const maskedRecipients = document.Recipient.map((recipient) => {
|
||||||
|
if (document.userId === user?.id) {
|
||||||
|
return recipient;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (recipient.email === user?.email) {
|
||||||
|
return recipient;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (recipient.token === token) {
|
||||||
|
return recipient;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...recipient,
|
||||||
|
token: '',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
...document,
|
||||||
|
Recipient: maskedRecipients,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -18,6 +18,7 @@ export const seedDatabase = async () => {
|
|||||||
create: {
|
create: {
|
||||||
name: 'Example User',
|
name: 'Example User',
|
||||||
email: 'example@documenso.com',
|
email: 'example@documenso.com',
|
||||||
|
emailVerified: new Date(),
|
||||||
password: hashSync('password'),
|
password: hashSync('password'),
|
||||||
roles: [Role.USER],
|
roles: [Role.USER],
|
||||||
},
|
},
|
||||||
@ -31,6 +32,7 @@ export const seedDatabase = async () => {
|
|||||||
create: {
|
create: {
|
||||||
name: 'Admin User',
|
name: 'Admin User',
|
||||||
email: 'admin@documenso.com',
|
email: 'admin@documenso.com',
|
||||||
|
emailVerified: new Date(),
|
||||||
password: hashSync('password'),
|
password: hashSync('password'),
|
||||||
roles: [Role.USER, Role.ADMIN],
|
roles: [Role.USER, Role.ADMIN],
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user