-
-
Notifications
You must be signed in to change notification settings - Fork 32
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Add support for filtering encrypted field by list of values #120
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -67,7 +67,7 @@ export function encryptOnWrite<Models extends string, Actions extends string>( | |
models, | ||
function encryptFieldValue({ | ||
fieldConfig, | ||
value: clearText, | ||
value: unHashedValue, | ||
path, | ||
model, | ||
field | ||
|
@@ -81,7 +81,15 @@ export function encryptOnWrite<Models extends string, Actions extends string>( | |
if (!fieldConfig.hash) { | ||
console.warn(warnings.whereConnectClauseNoHash(operation, path)) | ||
} else { | ||
const hash = hashString(clearText, fieldConfig.hash) | ||
const fieldConfigHash = fieldConfig.hash; | ||
|
||
let hash: string | string[]; | ||
// If unHashedValue is a list, hash each value | ||
if (typeof unHashedValue === 'object') { | ||
hash = (unHashedValue as string[]).map((value) => hashString(value, fieldConfigHash)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: Use the |
||
} else { | ||
hash = hashString(unHashedValue, fieldConfigHash); | ||
} | ||
debug.encryption( | ||
`Swapping encrypted search of ${model}.${field} with hash search under ${fieldConfig.hash.targetField} (hash: ${hash})` | ||
) | ||
|
@@ -90,12 +98,18 @@ export function encryptOnWrite<Models extends string, Actions extends string>( | |
return | ||
} | ||
} | ||
if (isOrderBy(path, field, clearText)) { | ||
|
||
// Encrypting list values is not yet supported | ||
if (typeof unHashedValue !== 'string') { | ||
return; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: Could we add a log point to this code path, for debugging? It's unlikely we'll hit it with Prisma's current API AFAIK, but it would make it easier to catch issues in the future. |
||
} | ||
|
||
if (isOrderBy(path, field, unHashedValue)) { | ||
// Remove unsupported orderBy clause on encrypted text | ||
// (makes no sense to sort ciphertext nor to encrypt 'asc' | 'desc') | ||
console.error(errors.orderByUnsupported(model, field)) | ||
debug.encryption( | ||
`Removing orderBy clause on ${model}.${field} at path \`${path}: ${clearText}\`` | ||
`Removing orderBy clause on ${model}.${field} at path \`${path}: ${unHashedValue}\`` | ||
) | ||
objectPath.del(draft.args, path) | ||
return | ||
|
@@ -104,11 +118,11 @@ export function encryptOnWrite<Models extends string, Actions extends string>( | |
return | ||
} | ||
try { | ||
const cipherText = encryptStringSync(clearText, keys.encryptionKey) | ||
const cipherText = encryptStringSync(unHashedValue, keys.encryptionKey) | ||
objectPath.set(draft.args, path, cipherText) | ||
debug.encryption(`Encrypted ${model}.${field} at path \`${path}\``) | ||
if (fieldConfig.hash) { | ||
const hash = hashString(clearText, fieldConfig.hash) | ||
const hash = hashString(unHashedValue, fieldConfig.hash) | ||
const hashPath = rewriteWritePath( | ||
path, | ||
field, | ||
|
@@ -176,9 +190,15 @@ export function decryptOnRead<Models extends string, Actions extends string>( | |
field | ||
}) { | ||
try { | ||
// Decrypting list values is not yet supported | ||
if (typeof cipherText !== 'string') { | ||
return; | ||
} | ||
|
||
if (!cloakedStringRegex.test(cipherText)) { | ||
return | ||
} | ||
|
||
const decryptionKey = findKeyForMessage(cipherText, keys.keychain) | ||
const clearText = decryptStringSync(cipherText, decryptionKey) | ||
objectPath.set(result, path, clearText) | ||
|
@@ -213,7 +233,7 @@ function rewriteHashedFieldPath( | |
) { | ||
const items = path.split('.').reverse() | ||
// Special case for `where field equals or not` clause | ||
if (items.includes('where') && items[1] === field && ['equals', 'not'].includes(items[0])) { | ||
if (items.includes('where') && items[1] === field && ['equals', 'not', 'in'].includes(items[0])) { | ||
items[1] = hashField | ||
return items.reverse().join('.') | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,7 +10,7 @@ interface VisitorState { | |
|
||
export interface TargetField { | ||
path: string | ||
value: string | ||
value: string | string[] | ||
model: string | ||
field: string | ||
fieldConfig: FieldConfiguration | ||
|
@@ -46,7 +46,11 @@ const makeVisitor = ( | |
if ( | ||
type === 'object' && | ||
key in model.fields && | ||
typeof (node as any)?.[specialSubField] === 'string' | ||
( | ||
// Used for where: { field: in: []} queries | ||
typeof (node as any)?.[specialSubField] === 'string' || | ||
typeof (node as any)?.[specialSubField] === 'object' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: Same test here, using |
||
) | ||
) { | ||
const value: string = (node as any)[specialSubField] | ||
const targetField: TargetField = { | ||
|
@@ -84,7 +88,7 @@ export function visitInputTargetFields< | |
) { | ||
traverseTree( | ||
params.args, | ||
makeVisitor(models, visitor, ['equals', 'set', 'not'], debug.encryption), | ||
makeVisitor(models, visitor, ['equals', 'set', 'not', 'in'], debug.encryption), | ||
{ | ||
currentModel: params.model! | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: Keep the original
clearText
name, as the value is not only used for hashing, but also for encryption in most (writing) cases.