57 lines
2.1 KiB
JavaScript
57 lines
2.1 KiB
JavaScript
/// <reference path="../pb_data/types.d.ts" />
|
|
// M20 — users.onRecordCreateRequest role guard.
|
|
//
|
|
// Closes the self-signup role-escalation hole: a client can no longer
|
|
// POST { role: 'technician', shopUserId: '<someone>' } to /api/collections/users
|
|
// to land inside another shop's technician list.
|
|
//
|
|
// Rule: role defaults to 'advisor' and shopUserId is cleared, UNLESS the
|
|
// email being registered matches a pending, unexpired technicianInvites
|
|
// record — in which case role='technician' and shopUserId comes from the
|
|
// invite (not the client).
|
|
//
|
|
// Idempotent: re-applying is safe because we only set values on create.
|
|
|
|
migrate(
|
|
(app) => {
|
|
app.onRecordCreateRequest((e) => {
|
|
if (e.collection.name !== 'users') return;
|
|
|
|
const record = e.record;
|
|
const email = (record.get('email') || '').toString().trim().toLowerCase();
|
|
if (!email) return;
|
|
|
|
// Look for a matching pending, unexpired invite.
|
|
let invite = null;
|
|
try {
|
|
invite = e.dao.findFirstRecordByFilter(
|
|
'technicianInvites',
|
|
`email = "${email.replace(/"/g, '')}" && status = "pending" && expiresAt >= @now`,
|
|
);
|
|
} catch {
|
|
invite = null;
|
|
}
|
|
|
|
if (invite) {
|
|
// Authorized technician signup — pin values from the invite.
|
|
record.set('role', 'technician');
|
|
record.set('shopUserId', invite.get('shopUserId') || '');
|
|
console.log(`[M20] technician signup authorized for ${email}`);
|
|
} else {
|
|
// Regular owner signup — force advisor, clear shop link.
|
|
record.set('role', 'advisor');
|
|
record.set('shopUserId', '');
|
|
console.log(`[M20] owner signup: role forced to advisor for ${email}`);
|
|
}
|
|
});
|
|
console.log('[M20] users create role guard installed');
|
|
},
|
|
(app) => {
|
|
// Best-effort rollback: we cannot easily unregister a JS hook in 0.39
|
|
// without restarting PB. Document that rolling this back requires a
|
|
// PB restart after removing the file. The down-migration is a no-op
|
|
// that logs the intent.
|
|
console.log('[M20] rollback requested — remove this migration file and restart PocketBase');
|
|
},
|
|
);
|