bugfix: clinic owner user access to other (dentist)staff's treatment plans terminated.
This commit is contained in:
@@ -30,6 +30,10 @@ import {
|
||||
} from './treatment.utils';
|
||||
import { assertCompleteToothProsthesisMap } from './lab-case-send.validation';
|
||||
import { hasEffectivePermission } from '../../common/membership-permissions';
|
||||
import {
|
||||
isActorTreatmentProvider,
|
||||
treatmentProviderScopeWhere,
|
||||
} from '../../common/treatment-provider-scope';
|
||||
|
||||
const sentLabCaseInclude = {
|
||||
treatment: {
|
||||
@@ -164,22 +168,12 @@ export class TreatmentsService {
|
||||
await this.assertCanReadTreatment(actorUserId, organizationId);
|
||||
await this.ensurePatientExists(patientId);
|
||||
|
||||
const membership = await this.getMembership(actorUserId, organizationId);
|
||||
const isOwner = membership?.isOwner ?? false;
|
||||
|
||||
const items = await this.prisma.treatment.findMany({
|
||||
where: {
|
||||
patientId,
|
||||
organizationId,
|
||||
details: { some: {} },
|
||||
...(isOwner
|
||||
? {}
|
||||
: {
|
||||
OR: [
|
||||
{ providerUserId: actorUserId },
|
||||
{ appointment: { is: { providerUserId: actorUserId } } },
|
||||
],
|
||||
}),
|
||||
...treatmentProviderScopeWhere(actorUserId),
|
||||
},
|
||||
include: treatmentInclude,
|
||||
orderBy: [{ treatmentAt: 'desc' }, { createdAt: 'desc' }],
|
||||
@@ -197,16 +191,13 @@ export class TreatmentsService {
|
||||
await this.assertCanReadTreatment(actorUserId, organizationId);
|
||||
await this.ensurePatientExists(patientId);
|
||||
|
||||
const membership = await this.getMembership(actorUserId, organizationId);
|
||||
const isOwner = membership?.isOwner ?? false;
|
||||
|
||||
const labCases = await this.prisma.labCase.findMany({
|
||||
where: {
|
||||
sentAt: { not: null },
|
||||
treatment: {
|
||||
patientId,
|
||||
organizationId,
|
||||
...this.treatmentAccessFilter(isOwner, actorUserId),
|
||||
...treatmentProviderScopeWhere(actorUserId),
|
||||
},
|
||||
},
|
||||
include: sentLabCaseInclude,
|
||||
@@ -224,15 +215,12 @@ export class TreatmentsService {
|
||||
async listUnreadLabCases(organizationId: string, actorUserId: string) {
|
||||
await this.assertCanReadTreatment(actorUserId, organizationId);
|
||||
|
||||
const membership = await this.getMembership(actorUserId, organizationId);
|
||||
const isOwner = membership?.isOwner ?? false;
|
||||
|
||||
const labCases = await this.prisma.labCase.findMany({
|
||||
where: {
|
||||
sentAt: { not: null },
|
||||
treatment: {
|
||||
organizationId,
|
||||
...this.treatmentAccessFilter(isOwner, actorUserId),
|
||||
...treatmentProviderScopeWhere(actorUserId),
|
||||
},
|
||||
},
|
||||
include: sentLabCaseInclude,
|
||||
@@ -258,17 +246,6 @@ export class TreatmentsService {
|
||||
return { success: true, data: this.sortLabCaseSummaries(summaries) };
|
||||
}
|
||||
|
||||
private treatmentAccessFilter(isOwner: boolean, actorUserId: string) {
|
||||
return isOwner
|
||||
? {}
|
||||
: {
|
||||
OR: [
|
||||
{ providerUserId: actorUserId },
|
||||
{ appointment: { is: { providerUserId: actorUserId } } },
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
private async mapSentLabCaseSummaries(
|
||||
labCases: SentLabCaseRow[],
|
||||
actorUserId: string,
|
||||
@@ -367,7 +344,6 @@ export class TreatmentsService {
|
||||
appointmentId,
|
||||
organizationId,
|
||||
actorUserId,
|
||||
false,
|
||||
);
|
||||
|
||||
const treatment = await this.prisma.treatment.findFirst({
|
||||
@@ -392,7 +368,6 @@ export class TreatmentsService {
|
||||
appointmentId,
|
||||
organizationId,
|
||||
actorUserId,
|
||||
true,
|
||||
);
|
||||
|
||||
for (const d of dto.details) {
|
||||
@@ -537,7 +512,6 @@ export class TreatmentsService {
|
||||
appointmentId,
|
||||
organizationId,
|
||||
actorUserId,
|
||||
true,
|
||||
);
|
||||
|
||||
const treatment = await this.prisma.treatment.findFirst({
|
||||
@@ -615,7 +589,13 @@ export class TreatmentsService {
|
||||
for (const [index, lc] of dto.labCases.entries()) {
|
||||
if (lc.id && sentLabCaseIds.has(lc.id)) {
|
||||
if (lc.dueDate !== undefined) {
|
||||
await this.updateLabCaseDueDateInTx(tx, lc.id, organizationId, lc.dueDate);
|
||||
await this.updateLabCaseDueDateInTx(
|
||||
tx,
|
||||
lc.id,
|
||||
organizationId,
|
||||
actorUserId,
|
||||
lc.dueDate,
|
||||
);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -710,7 +690,12 @@ export class TreatmentsService {
|
||||
treatment: { organizationId },
|
||||
},
|
||||
include: {
|
||||
treatment: { select: { providerUserId: true } },
|
||||
treatment: {
|
||||
select: {
|
||||
providerUserId: true,
|
||||
appointment: { select: { providerUserId: true } },
|
||||
},
|
||||
},
|
||||
sends: { select: { organizationId: true } },
|
||||
details: {
|
||||
include: {
|
||||
@@ -739,11 +724,8 @@ export class TreatmentsService {
|
||||
|
||||
assertCompleteToothProsthesisMap(labCase);
|
||||
|
||||
if (labCase.treatment.providerUserId !== actorUserId) {
|
||||
const membership = await this.getMembership(actorUserId, organizationId);
|
||||
if (!membership?.isOwner) {
|
||||
throw new ForbiddenException('Only the appointment provider can send this lab case');
|
||||
}
|
||||
if (!isActorTreatmentProvider(labCase.treatment, actorUserId)) {
|
||||
throw new ForbiddenException('Only the treatment provider can send this lab case');
|
||||
}
|
||||
|
||||
const linkedOrgIds = await this.getActiveLinkedOrganizationIds(organizationId);
|
||||
@@ -821,7 +803,13 @@ export class TreatmentsService {
|
||||
await this.assertCanEditTreatment(actorUserId, organizationId);
|
||||
|
||||
const updated = await this.prisma.$transaction(async (tx) => {
|
||||
await this.updateLabCaseDueDateInTx(tx, labCaseId, organizationId, dto.dueDate ?? null);
|
||||
await this.updateLabCaseDueDateInTx(
|
||||
tx,
|
||||
labCaseId,
|
||||
organizationId,
|
||||
actorUserId,
|
||||
dto.dueDate ?? null,
|
||||
);
|
||||
return tx.labCase.findFirstOrThrow({
|
||||
where: { id: labCaseId },
|
||||
include: {
|
||||
@@ -862,12 +850,16 @@ export class TreatmentsService {
|
||||
tx: Prisma.TransactionClient,
|
||||
labCaseId: string,
|
||||
organizationId: string,
|
||||
actorUserId: string,
|
||||
dueDateInput?: string | null,
|
||||
) {
|
||||
const labCase = await tx.labCase.findFirst({
|
||||
where: {
|
||||
id: labCaseId,
|
||||
treatment: { organizationId },
|
||||
treatment: {
|
||||
organizationId,
|
||||
...treatmentProviderScopeWhere(actorUserId),
|
||||
},
|
||||
sentAt: { not: null },
|
||||
},
|
||||
include: { tasks: { select: { status: true } } },
|
||||
@@ -902,7 +894,7 @@ export class TreatmentsService {
|
||||
actorUserId: string,
|
||||
) {
|
||||
await this.assertCanEditTreatment(actorUserId, organizationId);
|
||||
await this.ensureAppointmentProvider(appointmentId, organizationId, actorUserId, true);
|
||||
await this.ensureAppointmentProvider(appointmentId, organizationId, actorUserId);
|
||||
|
||||
if (!detailClientKey?.trim()) {
|
||||
throw new BadRequestException('detailClientKey is required');
|
||||
@@ -956,7 +948,14 @@ export class TreatmentsService {
|
||||
where: {
|
||||
id: attachmentId,
|
||||
OR: [
|
||||
{ detail: { treatment: { organizationId } } },
|
||||
{
|
||||
detail: {
|
||||
treatment: {
|
||||
organizationId,
|
||||
...treatmentProviderScopeWhere(actorUserId),
|
||||
},
|
||||
},
|
||||
},
|
||||
{ appointmentId: { not: null } },
|
||||
],
|
||||
},
|
||||
@@ -975,7 +974,11 @@ export class TreatmentsService {
|
||||
|
||||
if (!attachment.detail && attachment.appointmentId) {
|
||||
const appointment = await this.prisma.appointment.findFirst({
|
||||
where: { id: attachment.appointmentId, organizationId },
|
||||
where: {
|
||||
id: attachment.appointmentId,
|
||||
organizationId,
|
||||
providerUserId: actorUserId,
|
||||
},
|
||||
select: { id: true },
|
||||
});
|
||||
if (!appointment) {
|
||||
@@ -1228,7 +1231,6 @@ export class TreatmentsService {
|
||||
appointmentId: string,
|
||||
organizationId: string,
|
||||
actorUserId: string,
|
||||
requireProviderMatch: boolean,
|
||||
) {
|
||||
const appointment = await this.prisma.appointment.findFirst({
|
||||
where: { id: appointmentId, organizationId },
|
||||
@@ -1244,12 +1246,8 @@ export class TreatmentsService {
|
||||
throw new NotFoundException('Appointment not found');
|
||||
}
|
||||
|
||||
if (requireProviderMatch) {
|
||||
const membership = await this.getMembership(actorUserId, organizationId);
|
||||
const isOwner = membership?.isOwner ?? false;
|
||||
if (!isOwner && appointment.providerUserId !== actorUserId) {
|
||||
throw new ForbiddenException('You are not the provider for this appointment');
|
||||
}
|
||||
if (appointment.providerUserId !== actorUserId) {
|
||||
throw new ForbiddenException('You are not the provider for this appointment');
|
||||
}
|
||||
|
||||
return appointment;
|
||||
|
||||
Reference in New Issue
Block a user