Files
documenso/packages/lib/utils/organisations.ts
T
Catalin Pit baa2c51123 feat: add delegate document ownership option (#2272)
When using an API key created in a team context, the
documents/templates’ owner always defaults to the team API token
creator, rather than the actual uploader.

For example, John creates the API key for the team "Lawyers". Tom and
Maria use the API key to upload documents. All the uploaded documents
are attributed to John.

This makes it impossible to see who actually uploaded a document.

The new feature allows users to enable document ownership delegation
from the organization/team settings.
2025-12-23 22:08:54 +11:00

143 lines
4.1 KiB
TypeScript

import type { Organisation, OrganisationGlobalSettings, Prisma } from '@prisma/client';
import {
DocumentVisibility,
type OrganisationGroup,
type OrganisationMemberRole,
} from '@prisma/client';
import type { ORGANISATION_MEMBER_ROLE_MAP } from '@documenso/lib/constants/organisations-translations';
import { DEFAULT_DOCUMENT_DATE_FORMAT } from '../constants/date-formats';
import {
LOWEST_ORGANISATION_ROLE,
ORGANISATION_MEMBER_ROLE_HIERARCHY,
ORGANISATION_MEMBER_ROLE_PERMISSIONS_MAP,
} from '../constants/organisations';
import { DEFAULT_DOCUMENT_EMAIL_SETTINGS } from '../types/document-email';
export const isPersonalLayout = (organisations: Pick<Organisation, 'type'>[]) => {
return organisations.length === 1 && organisations[0].type === 'PERSONAL';
};
/**
* Determines whether a team member can execute a given action.
*
* @param action The action the user is trying to execute.
* @param role The current role of the user.
* @returns Whether the user can execute the action.
*/
export const canExecuteOrganisationAction = (
action: keyof typeof ORGANISATION_MEMBER_ROLE_PERMISSIONS_MAP,
role: keyof typeof ORGANISATION_MEMBER_ROLE_MAP,
) => {
return ORGANISATION_MEMBER_ROLE_PERMISSIONS_MAP[action].some((i) => i === role);
};
/**
* Compares the provided `currentUserRole` with the provided `roleToCheck` to determine
* whether the `currentUserRole` has permission to modify the `roleToCheck`.
*
* @param currentUserRole Role of the current user
* @param roleToCheck Role of another user to see if the current user can modify
* @returns True if the current user can modify the other user, false otherwise
*/
export const isOrganisationRoleWithinUserHierarchy = (
currentUserRole: keyof typeof ORGANISATION_MEMBER_ROLE_MAP,
roleToCheck: keyof typeof ORGANISATION_MEMBER_ROLE_MAP,
) => {
return ORGANISATION_MEMBER_ROLE_HIERARCHY[currentUserRole].some((i) => i === roleToCheck);
};
export const getHighestOrganisationRoleInGroup = (
groups: Pick<OrganisationGroup, 'type' | 'organisationRole'>[],
): OrganisationMemberRole => {
let highestOrganisationRole: OrganisationMemberRole = LOWEST_ORGANISATION_ROLE;
groups.forEach((group) => {
const currentRolePriority = ORGANISATION_MEMBER_ROLE_HIERARCHY[group.organisationRole].length;
const highestOrganisationRolePriority =
ORGANISATION_MEMBER_ROLE_HIERARCHY[highestOrganisationRole].length;
if (currentRolePriority > highestOrganisationRolePriority) {
highestOrganisationRole = group.organisationRole;
}
});
return highestOrganisationRole;
};
type BuildOrganisationWhereQueryOptions = {
organisationId: string | undefined;
userId: number;
roles?: OrganisationMemberRole[];
};
export const buildOrganisationWhereQuery = ({
organisationId,
userId,
roles,
}: BuildOrganisationWhereQueryOptions): Prisma.OrganisationWhereInput => {
// Note: Not using inline ternary since typesafety breaks for some reason.
if (!roles) {
return {
id: organisationId,
members: {
some: {
userId,
},
},
};
}
return {
id: organisationId,
members: {
some: {
userId,
organisationGroupMembers: {
some: {
group: {
organisationRole: {
in: roles,
},
},
},
},
},
},
};
};
export const generateDefaultOrganisationSettings = (): Omit<
OrganisationGlobalSettings,
'id' | 'organisation'
> => {
return {
documentVisibility: DocumentVisibility.EVERYONE,
documentLanguage: 'en',
documentTimezone: null, // Null means local timezone.
documentDateFormat: DEFAULT_DOCUMENT_DATE_FORMAT,
delegateDocumentOwnership: false,
includeSenderDetails: true,
includeSigningCertificate: true,
includeAuditLog: false,
typedSignatureEnabled: true,
uploadSignatureEnabled: true,
drawSignatureEnabled: true,
brandingEnabled: false,
brandingLogo: '',
brandingUrl: '',
brandingCompanyDetails: '',
emailId: null,
emailReplyTo: null,
// emailReplyToName: null,
emailDocumentSettings: DEFAULT_DOCUMENT_EMAIL_SETTINGS,
aiFeaturesEnabled: false,
};
};