mirror of
https://github.com/parse-community/parse-server
synced 2026-08-09 13:03:18 +00:00
48 lines
1.8 KiB
JavaScript
48 lines
1.8 KiB
JavaScript
// Apply optimistic locking for authData provider field changes. For each lockable
|
|
// top-level field in the original authData whose value differs from the incoming
|
|
// value, add an equality constraint for the original value to the update WHERE
|
|
// clause. Concurrent requests racing the same single-use token will only allow the
|
|
// first update to match; subsequent updates miss and surface as OBJECT_NOT_FOUND.
|
|
//
|
|
// Only fields whose values round-trip cleanly through both storage adapters are
|
|
// locked: primitives (string, number, boolean) and arrays. Date values and nested
|
|
// objects are skipped because their JSON representation differs between the
|
|
// MongoDB and Postgres adapters, and because Parse Server's query-key validator
|
|
// rejects deeper paths containing characters like `+` (e.g. phone-number keys).
|
|
// Locking the consumed single-use credential (the MFA token string or the
|
|
// recovery-code array) is sufficient — its removal invalidates the WHERE clause
|
|
// for concurrent writers.
|
|
export function applyAuthDataOptimisticLock(query, originalAuthData, newAuthData) {
|
|
if (!originalAuthData) {
|
|
return;
|
|
}
|
|
for (const provider of Object.keys(newAuthData)) {
|
|
const original = originalAuthData[provider];
|
|
if (!original || typeof original !== 'object') {
|
|
continue;
|
|
}
|
|
for (const [field, value] of Object.entries(original)) {
|
|
if (!isLockableAuthDataValue(value)) {
|
|
continue;
|
|
}
|
|
if (JSON.stringify(value) !== JSON.stringify(newAuthData[provider]?.[field])) {
|
|
query[`authData.${provider}.${field}`] = value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function isLockableAuthDataValue(value) {
|
|
if (value === null || value === undefined) {
|
|
return false;
|
|
}
|
|
const t = typeof value;
|
|
if (t === 'string' || t === 'number' || t === 'boolean') {
|
|
return true;
|
|
}
|
|
if (Array.isArray(value)) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|