From c5612d50fecdb9af47616fb3146317466d859981 Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Thu, 27 Oct 2022 14:38:41 +0200 Subject: [PATCH] #2774 Locally encrypted properties can be used in "in" queries --- .../server/expression/InExpression.java | 61 ++++++++++++------- .../org/tests/basic/encrypt/TestEncrypt.java | 11 +++- .../basic/encrypt/TestEncryptClientSide.java | 12 +++- 3 files changed, 57 insertions(+), 27 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InExpression.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InExpression.java index 2109b2a92..bf3f803db 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/InExpression.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/InExpression.java @@ -117,29 +117,39 @@ public final class InExpression extends AbstractExpression implements IdInCommon } } ElPropertyValue prop = getElProp(request); - if (prop != null && !prop.isAssocId()) { - prop = null; - } - if (prop == null) { - if (bindValues.size() > 0) { - // if we have no property, we wrap them in a multi value wrapper. - // later the binder will decide, which bind strategy to use. - request.addBindValue(new MultiValueWrapper(bindValues)); - } - } else { - List idList = new ArrayList<>(); + List values = bindValues; + if (prop != null && prop.isAssocId()) { + values = new ArrayList<>(); for (Object bindValue : bindValues) { // extract the id values from the bean Object[] ids = prop.assocIdValues((EntityBean) bindValue); if (ids != null) { - Collections.addAll(idList, ids); + Collections.addAll(values, ids); } } - if (!idList.isEmpty()) { - request.addBindValue(new MultiValueWrapper(idList)); - } } + if (values.isEmpty()) { + // nothing in in-query + return; + } else if (prop.isDbEncrypted()) { + // bind the key as well as the value + String encryptKey = prop.beanProperty().encryptKey().getStringValue(); + request.addBindEncryptKey(encryptKey); + } else if (prop.isLocalEncrypted()) { + List encValues = new ArrayList<>(values.size()); + for (Object value : values) { + encValues.add(prop.localEncrypt(value)); + } + // this is most likely binary garbage, so don't add it to the bind log + request.addBindEncryptKey(new MultiValueWrapper(encValues)); + return; + } + + // if we have no property, we wrap them in a multi value wrapper. + // later the binder will decide, which bind strategy to use. + request.addBindValue(new MultiValueWrapper(values)); + } @Override @@ -154,17 +164,22 @@ public final class InExpression extends AbstractExpression implements IdInCommon } ElPropertyValue prop = getElProp(request); - if (prop != null && !prop.isAssocId()) { - prop = null; - } if (prop != null) { - request.append(prop.assocIdInExpr(propName)); - request.append(prop.assocIdInValueExpr(not, bindValues.size())); - } else { - request.append(propName); - request.appendInExpression(not, bindValues); + if (prop.isAssocId()) { + request.append(prop.assocIdInExpr(propName)); + request.append(prop.assocIdInValueExpr(not, bindValues.size())); + } + if (prop.isDbEncrypted()) { + String dsql = prop.beanProperty().decryptProperty(propName); + request.append(dsql); + request.appendInExpression(not, bindValues); + return; + } } + request.append(propName); + request.appendInExpression(not, bindValues); + } /** diff --git a/ebean-test/src/test/java/org/tests/basic/encrypt/TestEncrypt.java b/ebean-test/src/test/java/org/tests/basic/encrypt/TestEncrypt.java index 9998c1669..52356064e 100644 --- a/ebean-test/src/test/java/org/tests/basic/encrypt/TestEncrypt.java +++ b/ebean-test/src/test/java/org/tests/basic/encrypt/TestEncrypt.java @@ -1,13 +1,13 @@ package org.tests.basic.encrypt; -import io.ebean.xtest.BaseTestCase; import io.ebean.DB; import io.ebean.SqlRow; -import io.ebean.xtest.ForPlatform; -import io.ebean.xtest.IgnorePlatform; import io.ebean.annotation.Platform; import io.ebean.config.dbplatform.DbEncrypt; import io.ebean.test.LoggedSql; +import io.ebean.xtest.BaseTestCase; +import io.ebean.xtest.ForPlatform; +import io.ebean.xtest.IgnorePlatform; import io.ebeaninternal.api.SpiEbeanServer; import org.junit.jupiter.api.Test; import org.tests.model.basic.EBasicEncrypt; @@ -149,6 +149,11 @@ public class TestEncrypt extends BaseTestCase { assertEquals(1, list.size()); + list = DB.find(EBasicEncrypt.class).where() + .in("description", "moddesc").findList(); + + assertEquals(1, list.size()); + list = DB.find(EBasicEncrypt.class).where().startsWith("description", "modde").findList(); assertEquals(1, list.size()); diff --git a/ebean-test/src/test/java/org/tests/basic/encrypt/TestEncryptClientSide.java b/ebean-test/src/test/java/org/tests/basic/encrypt/TestEncryptClientSide.java index c67514cc5..0010b4c60 100644 --- a/ebean-test/src/test/java/org/tests/basic/encrypt/TestEncryptClientSide.java +++ b/ebean-test/src/test/java/org/tests/basic/encrypt/TestEncryptClientSide.java @@ -1,7 +1,7 @@ package org.tests.basic.encrypt; -import io.ebean.xtest.BaseTestCase; import io.ebean.DB; +import io.ebean.xtest.BaseTestCase; import org.junit.jupiter.api.Test; import org.tests.model.basic.EBasicEncryptClient; @@ -50,6 +50,16 @@ public class TestEncryptClientSide extends BaseTestCase { assertThat(found).isNotNull(); assertThat(found.getDescription()).isEqualTo("goodbye"); + found = DB.find(EBasicEncryptClient.class) + .where() + .in("description", "goodbye") + .in("status", EBasicEncryptClient.Status.TWO) + .in("dob", today) + .findOne(); + assertThat(found).isNotNull(); + assertThat(found.getDescription()).isEqualTo("goodbye"); + + DB.delete(found); } }