fix: preserve invitations for admins with a family
This commit is contained in:
@@ -466,7 +466,7 @@ export async function resendFamilyInvitationAction(
|
|||||||
if (!(await rateLimit(`resend-invite:${session.user.kitaId}:${userId}`, 3, 3600)).allowed)
|
if (!(await rateLimit(`resend-invite:${session.user.kitaId}:${userId}`, 3, 3600)).allowed)
|
||||||
return { error: "Bitte versuche es später erneut." };
|
return { error: "Bitte versuche es später erneut." };
|
||||||
const recipient = await prisma.user.findFirst({
|
const recipient = await prisma.user.findFirst({
|
||||||
where: { id: userId, kitaId: session.user.kitaId, role: UserRole.ELTERN,
|
where: { id: userId, kitaId: session.user.kitaId, familyId: { not: null },
|
||||||
emailVerifiedAt: null, passwordHash: "", disabledAt: null },
|
emailVerifiedAt: null, passwordHash: "", disabledAt: null },
|
||||||
select: { email: true },
|
select: { email: true },
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export async function queueFamilyInvitation(
|
|||||||
invitedById: string,
|
invitedById: string,
|
||||||
) {
|
) {
|
||||||
const user = await tx.user.findUniqueOrThrow({ where: { id: userId } });
|
const user = await tx.user.findUniqueOrThrow({ where: { id: userId } });
|
||||||
if (!user.kitaId || !user.familyId || user.role !== "ELTERN" || user.emailVerifiedAt || user.passwordHash || user.disabledAt)
|
if (!user.kitaId || !user.familyId || user.emailVerifiedAt || user.passwordHash || user.disabledAt)
|
||||||
throw new Error("Dieses Konto benötigt keine Familieneinladung.");
|
throw new Error("Dieses Konto benötigt keine Familieneinladung.");
|
||||||
const existing = await tx.invitation.findFirst({
|
const existing = await tx.invitation.findFirst({
|
||||||
where: {
|
where: {
|
||||||
@@ -99,7 +99,6 @@ export async function deliverFamilyInvitations(
|
|||||||
user.emailVerifiedAt ||
|
user.emailVerifiedAt ||
|
||||||
user.disabledAt ||
|
user.disabledAt ||
|
||||||
user.passwordHash !== "" ||
|
user.passwordHash !== "" ||
|
||||||
user.role !== "ELTERN" ||
|
|
||||||
!token ||
|
!token ||
|
||||||
token.identifier !== user.id ||
|
token.identifier !== user.id ||
|
||||||
token.expires <= now
|
token.expires <= now
|
||||||
@@ -170,7 +169,7 @@ export async function resendFamilyInvitation(
|
|||||||
where: { id: userId, kitaId, familyId: { not: null } },
|
where: { id: userId, kitaId, familyId: { not: null } },
|
||||||
});
|
});
|
||||||
if (!user) return { error: "Elternkonto wurde nicht gefunden." };
|
if (!user) return { error: "Elternkonto wurde nicht gefunden." };
|
||||||
if (user.emailVerifiedAt || user.passwordHash || user.disabledAt || user.role !== "ELTERN")
|
if (user.emailVerifiedAt || user.passwordHash || user.disabledAt)
|
||||||
return { error: "Keine offene Einladung gefunden." };
|
return { error: "Keine offene Einladung gefunden." };
|
||||||
if (expectedEmail && user.email !== expectedEmail)
|
if (expectedEmail && user.email !== expectedEmail)
|
||||||
return { error: "Die Einladung hat sich geändert. Bitte lade die Seite neu." };
|
return { error: "Die Einladung hat sich geändert. Bitte lade die Seite neu." };
|
||||||
|
|||||||
@@ -315,6 +315,33 @@ test('changing a parent email revokes sessions and requires fresh mailbox activa
|
|||||||
assert.equal((await accept({ attempt: 0 }, form(renewedInvite))).errors,undefined);
|
assert.equal((await accept({ attempt: 0 }, form(renewedInvite))).errors,undefined);
|
||||||
assert.ok((await prisma.user.findUnique({ where: { id: parentA.id } })).emailVerifiedAt);
|
assert.ok((await prisma.user.findUnique({ where: { id: parentA.id } })).emailVerifiedAt);
|
||||||
});
|
});
|
||||||
|
test('family admins keep their role when a changed mailbox is invited and activated', async () => {
|
||||||
|
const family = await prisma.family.create({ data: { kitaId: kitaA.id, name: 'Admin family' } });
|
||||||
|
const child = await prisma.child.create({ data: { kitaId: kitaA.id, familyId: family.id, firstName: 'Admin', lastName: 'Child' } });
|
||||||
|
const user = await prisma.user.create({ data: { email: `family-admin.${prefix}@example.invalid`, firstName: 'Family', lastName: 'Admin',
|
||||||
|
role: 'ADMIN', kitaId: kitaA.id, familyId: family.id, passwordHash: passHash,
|
||||||
|
emailVerifiedAt: new Date(), privacyPolicyAcceptedAt: new Date() } });
|
||||||
|
try {
|
||||||
|
const load = loader({ '@/auth': { auth: async () => session(adminA), signIn: async () => {} }, '@/lib/mail': emailMock });
|
||||||
|
const actions = load('src/app/dashboard/families/actions.ts');
|
||||||
|
const updated = await actions.updateFamilyAction({ familyId: family.id, familyName: 'Family A',
|
||||||
|
parents: [{ id: user.id, firstName: 'Family', lastName: 'Admin', email: `changed-family-admin.${prefix}@example.invalid` }],
|
||||||
|
children: [{ id: child.id, firstName: 'A', lastName: 'Test' }] });
|
||||||
|
assert.equal(updated.success,true,updated.error); assert.equal(updated.warning,undefined);
|
||||||
|
assert.equal((await prisma.user.findUnique({ where: { id: user.id } })).role,'ADMIN');
|
||||||
|
await prisma.invitation.update({ where: { userId: user.id }, data: { sentAt: new Date(Date.now() - 61_000) } });
|
||||||
|
assert.equal((await actions.resendParentInvite(user.id)).success,true);
|
||||||
|
const token = emails.at(-1).react.props.inviteLink.split('/').at(-1);
|
||||||
|
const accepted = await load('src/app/invite/[token]/actions.ts').acceptInviteAction({ attempt: 0 }, fields({ token,
|
||||||
|
password: 'Family-admin-new-password', confirmPassword: 'Family-admin-new-password', acceptPrivacyPolicy: 'on' }));
|
||||||
|
assert.equal(accepted.errors,undefined);
|
||||||
|
const activated = await prisma.user.findUnique({ where: { id: user.id } });
|
||||||
|
assert.equal(activated.role,'ADMIN'); assert.ok(activated.emailVerifiedAt);
|
||||||
|
} finally {
|
||||||
|
await prisma.verificationToken.deleteMany({ where: { identifier: user.id } });
|
||||||
|
await prisma.family.delete({ where: { id: family.id } });
|
||||||
|
}
|
||||||
|
});
|
||||||
test('resending an invite cannot issue an active link to an obsolete mailbox', async () => {
|
test('resending an invite cannot issue an active link to an obsolete mailbox', async () => {
|
||||||
const user = await prisma.user.create({ data: { email: `resend-race.${prefix}@example.invalid`, firstName: 'Race', lastName: 'Test',
|
const user = await prisma.user.create({ data: { email: `resend-race.${prefix}@example.invalid`, firstName: 'Race', lastName: 'Test',
|
||||||
role: 'ELTERN', kitaId: kitaA.id, familyId: familyA.id, passwordHash: '' } });
|
role: 'ELTERN', kitaId: kitaA.id, familyId: familyA.id, passwordHash: '' } });
|
||||||
|
|||||||
Reference in New Issue
Block a user