diff --git a/NEWS.md b/NEWS.md index 0091a3b26..004ea5a50 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,15 @@ +## 33.2.10 2022-05-31 + +Fixes: + +* [RMB-913](https://issues.folio.org/browse/RMB-913) Fail if PostgreSQL version is too old +* [RMB-914](https://issues.folio.org/browse/RMB-914) Update raml-util to avoid localhost fetch RMB-914 +* [RMB-915](https://issues.folio.org/browse/RMB-915) public PostgresClient#getTenantId() +* [RMB-917](https://issues.folio.org/browse/RMB-917) id == "", foreignKeyId == "" should never match +* [RMB-918](https://issues.folio.org/browse/RMB-918) Fix Jakarta Expression Language validation (CVE-2021-28170) +* [RMB-919](https://issues.folio.org/browse/RMB-919) Delete by CQL rejects missing or empty CQL +* [RMB-920](https://issues.folio.org/browse/RMB-920) Upgrade dependencies: Vert.x 4.3.1, ... + ## 33.2.9 2022-04-22 Fix: diff --git a/cql2pgjson-cli/pom.xml b/cql2pgjson-cli/pom.xml index b0934f0d6..f11739e53 100644 --- a/cql2pgjson-cli/pom.xml +++ b/cql2pgjson-cli/pom.xml @@ -6,7 +6,7 @@ org.folio raml-module-builder - 33.2.10-SNAPSHOT + 33.2.11-SNAPSHOT diff --git a/cql2pgjson/pom.xml b/cql2pgjson/pom.xml index c173f5e24..30a336254 100644 --- a/cql2pgjson/pom.xml +++ b/cql2pgjson/pom.xml @@ -7,7 +7,7 @@ org.folio raml-module-builder - 33.2.10-SNAPSHOT + 33.2.11-SNAPSHOT @@ -89,7 +89,13 @@ org.postgresql postgresql - 42.3.2 + 42.3.6 + test + + + + org.folio.okapi + okapi-testing test diff --git a/cql2pgjson/src/main/java/org/folio/cql2pgjson/CQL2PgJSON.java b/cql2pgjson/src/main/java/org/folio/cql2pgjson/CQL2PgJSON.java index 3748b21bb..bdce1e900 100644 --- a/cql2pgjson/src/main/java/org/folio/cql2pgjson/CQL2PgJSON.java +++ b/cql2pgjson/src/main/java/org/folio/cql2pgjson/CQL2PgJSON.java @@ -608,9 +608,6 @@ private String pgId(CQLTermNode node, String columnName) throws QueryValidationE + "UUID " + columnName + " only supports '=', '==', and '<>' (possibly with right truncation)"); } - if (StringUtils.isEmpty(term)) { - term = "*"; - } if ("*".equals(term) && "id".equals(columnName)) { return equals ? "true" : "false"; // no need to check // since id is a mandatory field, so diff --git a/cql2pgjson/src/test/java/org/folio/cql2pgjson/CQL2PgJSONTest.java b/cql2pgjson/src/test/java/org/folio/cql2pgjson/CQL2PgJSONTest.java index 470b238f1..e00f1431e 100644 --- a/cql2pgjson/src/test/java/org/folio/cql2pgjson/CQL2PgJSONTest.java +++ b/cql2pgjson/src/test/java/org/folio/cql2pgjson/CQL2PgJSONTest.java @@ -800,12 +800,11 @@ public void toSqlException() throws QueryValidationException { @Test @Parameters({ - "id=*, true", - "id=\"\", true", + "id=*, true", + "id=\"\", false /* id == invalid UUID */", // exact (field) match, empty string never matches UUID "groupId=*, (groupId BETWEEN '00000000-0000-0000-0000-000000000000' " + "AND 'ffffffff-ffff-ffff-ffff-ffffffffffff')", - "groupId=\"\", (groupId BETWEEN '00000000-0000-0000-0000-000000000000' " - + "AND 'ffffffff-ffff-ffff-ffff-ffffffffffff')", + "groupId=\"\", false /* groupId == invalid UUID */", "id=\"11111111-1111-1111-1111-111111111111\", id='11111111-1111-1111-1111-111111111111'", "id=\"2*\", (id BETWEEN '20000000-0000-0000-0000-000000000000' " + "AND '2fffffff-ffff-ffff-ffff-ffffffffffff')", @@ -850,8 +849,9 @@ public void idColumnSort(String cql, String expectedSql) throws CQL2PgJSONExcept "id=11111111-1111-1111-1111-11111111111 #", "id=11111111-1111-1111-1111-1111111111111 #", "id=11111111-1111-1111-1111-111111111111-1 #", - "id=\"\" # Jo Jane; Ka Keller; Lea Long", - "id<>\"\" #", + "id=\"\" #", // exact match, empty string never matches UUID + "id==\"\" #", + "id<>\"\" # Jo Jane; Ka Keller; Lea Long", "id=1* # Jo Jane", "id=1** # Jo Jane", "id=1*** # Jo Jane", diff --git a/dbschema/pom.xml b/dbschema/pom.xml index 32850fe9d..105b3f56b 100644 --- a/dbschema/pom.xml +++ b/dbschema/pom.xml @@ -7,7 +7,7 @@ org.folio raml-module-builder - 33.2.10-SNAPSHOT + 33.2.11-SNAPSHOT diff --git a/domain-models-api-aspects/pom.xml b/domain-models-api-aspects/pom.xml index a3e296654..a3a5b9a19 100644 --- a/domain-models-api-aspects/pom.xml +++ b/domain-models-api-aspects/pom.xml @@ -3,7 +3,7 @@ org.folio raml-module-builder - 33.2.10-SNAPSHOT + 33.2.11-SNAPSHOT domain-models-api-aspects @@ -40,7 +40,7 @@ org.glassfish - javax.el + jakarta.el org.apache.commons @@ -57,7 +57,7 @@ org.javassist javassist - 3.28.0-GA + 3.29.0-GA diff --git a/domain-models-api-interfaces/pom.xml b/domain-models-api-interfaces/pom.xml index ebe7c7084..f30b9f925 100644 --- a/domain-models-api-interfaces/pom.xml +++ b/domain-models-api-interfaces/pom.xml @@ -3,7 +3,7 @@ org.folio raml-module-builder - 33.2.10-SNAPSHOT + 33.2.11-SNAPSHOT domain-models-api-interfaces diff --git a/domain-models-api-interfaces/ramls/raml-util b/domain-models-api-interfaces/ramls/raml-util index e20e19094..967f6369c 160000 --- a/domain-models-api-interfaces/ramls/raml-util +++ b/domain-models-api-interfaces/ramls/raml-util @@ -1 +1 @@ -Subproject commit e20e1909498749aeaee2e9f77ad1d71aea80992f +Subproject commit 967f6369c1186475243e309c45f3e043a329bbab diff --git a/domain-models-maven-plugin/pom.xml b/domain-models-maven-plugin/pom.xml index a01520cda..208fc51e7 100644 --- a/domain-models-maven-plugin/pom.xml +++ b/domain-models-maven-plugin/pom.xml @@ -3,7 +3,7 @@ org.folio raml-module-builder - 33.2.10-SNAPSHOT + 33.2.11-SNAPSHOT domain-models-maven-plugin Domain Model Generator that reads RAML files and writes Java files diff --git a/domain-models-runtime-it/pom.xml b/domain-models-runtime-it/pom.xml index 21ab15ebe..fa4dfbe08 100644 --- a/domain-models-runtime-it/pom.xml +++ b/domain-models-runtime-it/pom.xml @@ -6,7 +6,7 @@ org.folio raml-module-builder - 33.2.10-SNAPSHOT + 33.2.11-SNAPSHOT diff --git a/domain-models-runtime-it/ramls/raml-util b/domain-models-runtime-it/ramls/raml-util index e20e19094..967f6369c 160000 --- a/domain-models-runtime-it/ramls/raml-util +++ b/domain-models-runtime-it/ramls/raml-util @@ -1 +1 @@ -Subproject commit e20e1909498749aeaee2e9f77ad1d71aea80992f +Subproject commit 967f6369c1186475243e309c45f3e043a329bbab diff --git a/domain-models-runtime/pom.xml b/domain-models-runtime/pom.xml index b657334ee..3b128d3ca 100644 --- a/domain-models-runtime/pom.xml +++ b/domain-models-runtime/pom.xml @@ -6,7 +6,7 @@ org.folio raml-module-builder - 33.2.10-SNAPSHOT + 33.2.11-SNAPSHOT diff --git a/domain-models-runtime/src/main/java/org/folio/rest/impl/TenantAPI.java b/domain-models-runtime/src/main/java/org/folio/rest/impl/TenantAPI.java index c7c100c8a..171152d6b 100644 --- a/domain-models-runtime/src/main/java/org/folio/rest/impl/TenantAPI.java +++ b/domain-models-runtime/src/main/java/org/folio/rest/impl/TenantAPI.java @@ -49,6 +49,25 @@ PostgresClient postgresClient(Context context) { return PostgresClient.getInstance(context.owner()); } + Future requirePostgres(Context context, String minNum, String minVersion) { + return + postgresClient(context) + .select("SELECT current_setting('server_version_num') AS num, current_setting('server_version') AS version") + .map(rowSet -> { + String num = rowSet.iterator().next().getString("num"); + String version = rowSet.iterator().next().getString("version"); + if (minNum.compareTo(num) > 0) { + throw new UnsupportedOperationException( + "Expected PostgreSQL server version " + minVersion + " or later but found " + version); + } + return null; + }); + } + + Future requirePostgres12(Context context) { + return requirePostgres(context, "120000", "12.0"); + } + Future tenantExists(Context context, String tenantId){ /* connect as user in postgres-conf.json file (super user) - so that all commands will be available */ return postgresClient(context).select( @@ -207,7 +226,8 @@ private void postTenant(boolean async, TenantAttributes tenantAttributes, Map tenantExists(context, tenantId)) .compose(exists -> sqlFile(context, tenantId, tenantAttributes, exists)) .onFailure(cause -> { log.error(cause.getMessage(), cause); diff --git a/domain-models-runtime/src/main/java/org/folio/rest/persist/PgUtil.java b/domain-models-runtime/src/main/java/org/folio/rest/persist/PgUtil.java index 0bbbc130d..320c29e9b 100644 --- a/domain-models-runtime/src/main/java/org/folio/rest/persist/PgUtil.java +++ b/domain-models-runtime/src/main/java/org/folio/rest/persist/PgUtil.java @@ -25,6 +25,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.ws.rs.core.Response; +import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.folio.dbschema.ObjectMapperTool; @@ -918,17 +919,16 @@ static Future get(PreparedCQL preparedCql, Class clazz, Clas } } - /** - * Delete records by CQL. - * @param table the table that contains the records - * @param cql the CQL query for filtering the records - * @param okapiHeaders http headers provided by okapi - * @param vertxContext the current context - * @param responseDelegateClass the ResponseDelegate class generated as defined by the RAML file, - * must have these methods: respond204(), respond400WithTextPlain(Object), respond500WithTextPlain(Object). - * @param asyncResultHandler where to return the result created by the responseDelegateClass - */ - @SuppressWarnings({"unchecked", "squid:S107"}) // Method has >7 parameters + /** + * Delete records by CQL. + * @param table the table that contains the records + * @param cql the CQL query for filtering the records, required + * @param okapiHeaders http headers provided by okapi + * @param vertxContext the current context + * @param responseDelegateClass the ResponseDelegate class generated as defined by the RAML file, + * must have these methods: respond204(), respond400WithTextPlain(Object), respond500WithTextPlain(Object). + * @param asyncResultHandler where to return the result created by the responseDelegateClass + */ public static void delete(String table, String cql, Map okapiHeaders, Context vertxContext, @@ -937,17 +937,16 @@ public static void delete(String table, delete(table, cql, okapiHeaders, vertxContext, responseDelegateClass).onComplete(asyncResultHandler); } - /** + /** * Delete records by CQL. - * @param table the table that contains the records - * @param cql the CQL query for filtering the records - * @param okapiHeaders http headers provided by okapi - * @param vertxContext the current context - * @param responseDelegateClass the ResponseDelegate class generated as defined by the RAML file, -* must have these methods: respond204(), respond400WithTextPlain(Object), respond500WithTextPlain(Object). - * @return future where to return the result created by the responseDelegateClass - */ - @SuppressWarnings({"unchecked", "squid:S107"}) // Method has >7 parameters + * @param table the table that contains the records + * @param cql the CQL query for filtering the records, required + * @param okapiHeaders http headers provided by okapi + * @param vertxContext the current context + * @param responseDelegateClass the ResponseDelegate class generated as defined by the RAML file, + * must have these methods: respond204(), respond400WithTextPlain(Object), respond500WithTextPlain(Object). + * @return future where to return the result created by the responseDelegateClass + */ public static Future delete(String table, String cql, Map okapiHeaders, Context vertxContext, @@ -972,6 +971,10 @@ public static Future delete(String table, } try { + if (StringUtils.isBlank(cql)) { + return response("query with CQL expression is required", respond400, respond500); + } + CQL2PgJSON cql2pgJson = new CQL2PgJSON(table + "." + JSON_COLUMN); CQLWrapper cqlWrapper = new CQLWrapper(cql2pgJson, cql, -1, -1); PreparedCQL preparedCql = new PreparedCQL(table, cqlWrapper, okapiHeaders); @@ -1003,7 +1006,7 @@ public static Future delete(String table, } } - /** + /** * Get a record by id. * *

All exceptions are caught and reported via the asyncResultHandler. diff --git a/domain-models-runtime/src/main/java/org/folio/rest/persist/PostgresClient.java b/domain-models-runtime/src/main/java/org/folio/rest/persist/PostgresClient.java index bea8887a4..8dd7dc590 100644 --- a/domain-models-runtime/src/main/java/org/folio/rest/persist/PostgresClient.java +++ b/domain-models-runtime/src/main/java/org/folio/rest/persist/PostgresClient.java @@ -4247,7 +4247,7 @@ static String getModuleName(final String className) { /** * @return the tenantId of this PostgresClient */ - String getTenantId() { + public String getTenantId() { return tenantId; } diff --git a/domain-models-runtime/src/test/java/org/folio/rest/impl/TenantAPIIT.java b/domain-models-runtime/src/test/java/org/folio/rest/impl/TenantAPIIT.java index eff8daf18..901d91891 100644 --- a/domain-models-runtime/src/test/java/org/folio/rest/impl/TenantAPIIT.java +++ b/domain-models-runtime/src/test/java/org/folio/rest/impl/TenantAPIIT.java @@ -3,6 +3,7 @@ import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.startsWith; import static org.mockito.Mockito.*; import java.io.IOException; @@ -260,6 +261,13 @@ private void testMetadata(TestContext context) { assertThat(book.getMetadata(), is(nullValue())); } + @Test + public void requirePostgres(TestContext context) { + new TenantAPI().requirePostgres(vertx.getOrCreateContext(), "990000", "99.0") + .onComplete(context.asyncAssertFailure( + e -> assertThat(e.getMessage(), startsWith("Expected PostgreSQL server version 99.0 or later")))); + } + @Test public void previousSchemaSqlExistsTrue(TestContext context) { TenantAPI tenantAPI = new TenantAPI(); @@ -294,6 +302,11 @@ PostgresClient postgresClient(Context context) { return postgresClient; } + @Override + Future requirePostgres12(Context context) { + return Future.succeededFuture(); + } + @Override Future tenantExists(Context context, String tenantId) { return Future.succeededFuture(false); @@ -318,6 +331,11 @@ PostgresClient postgresClient(Context context) { return postgresClient; } + @Override + Future requirePostgres12(Context context) { + return Future.succeededFuture(); + } + @Override Future tenantExists(Context context, String tenantId) { return Future.succeededFuture(false); diff --git a/domain-models-runtime/src/test/java/org/folio/rest/persist/PgUtilIT.java b/domain-models-runtime/src/test/java/org/folio/rest/persist/PgUtilIT.java index c01fc1017..db2367b5b 100644 --- a/domain-models-runtime/src/test/java/org/folio/rest/persist/PgUtilIT.java +++ b/domain-models-runtime/src/test/java/org/folio/rest/persist/PgUtilIT.java @@ -413,6 +413,27 @@ public void deleteByCQLNullHeaders(TestContext testContext) { asyncAssertSuccess(testContext, 400, "null")); } + @Test + public void deleteByCQLWithoutCql(TestContext testContext) { + PgUtil.delete("users", null, + null, vertx.getOrCreateContext(), Users.DeleteUsersByUserIdResponse.class, + asyncAssertSuccess(testContext, 400, "query with CQL expression is required")); + } + + @Test + public void deleteByCQLWithEmptyCql(TestContext testContext) { + PgUtil.delete("users", "", + null, vertx.getOrCreateContext(), Users.DeleteUsersByUserIdResponse.class, + asyncAssertSuccess(testContext, 400, "query with CQL expression is required")); + } + + @Test + public void deleteByCQLWithWhitespaceCql(TestContext testContext) { + PgUtil.delete("users", "\t\n \t\n ", + null, vertx.getOrCreateContext(), Users.DeleteUsersByUserIdResponse.class, + asyncAssertSuccess(testContext, 400, "query with CQL expression is required")); + } + @Test public void deleteByCQLOK(TestContext testContext) { PostgresClient pg = PostgresClient.getInstance(vertx, "testtenant"); @@ -453,7 +474,6 @@ public void deleteByCQLOK(TestContext testContext) { @Test public void deleteByCQLSyntaxError(TestContext testContext) { - PostgresClient pg = PostgresClient.getInstance(vertx, "testtenant"); PgUtil.delete("users", "username==", okapiHeaders, vertx.getOrCreateContext(), Users.DeleteUsersByUserIdResponse.class, asyncAssertSuccess(testContext, 400, "expected index or term, got EOF")); @@ -461,7 +481,6 @@ public void deleteByCQLSyntaxError(TestContext testContext) { @Test public void deleteByCQLBadTable(TestContext testContext) { - PostgresClient pg = PostgresClient.getInstance(vertx, "testtenant"); PgUtil.delete("users1", "username==delete_test", okapiHeaders, vertx.getOrCreateContext(), Users.DeleteUsersByUserIdResponse.class, asyncAssertSuccess(testContext, 400, "relation \"testtenant_raml_module_builder.users1\" does not exist")); diff --git a/pom.xml b/pom.xml index 08f159892..caf6a24e6 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 org.folio raml-module-builder - 33.2.10-SNAPSHOT + 33.2.11-SNAPSHOT pom raml-module-builder @@ -30,8 +30,8 @@ 1.9.7 5.8.1 3.8.4 - 4.2.6 - 1.6.3 + 4.3.1 + 1.8.4 @@ -54,14 +54,7 @@ com.google.guava guava - 31.0.1-jre - - - com.fasterxml.jackson - jackson-bom - 2.13.2.20220324 - pom - import + 31.1-jre io.vertx @@ -80,7 +73,7 @@ io.rest-assured rest-assured - 4.5.1 + 5.1.0 javax.el @@ -171,13 +164,10 @@ org.mockito - mockito-core - 4.3.1 - - - org.mockito - mockito-junit-jupiter - 4.3.1 + mockito-bom + 4.6.0 + pom + import org.glassfish.jersey.media @@ -192,7 +182,7 @@ org.hibernate.validator hibernate-validator - 6.2.2.Final + 6.2.3.Final jakarta.validation @@ -202,13 +192,18 @@ org.glassfish - javax.el - 3.0.1-b12 + jakarta.el + 3.0.4 org.folio.okapi okapi-common - 4.12.0 + 4.14.1 + + + org.folio.okapi + okapi-testing + 4.14.1 org.apache.commons @@ -218,7 +213,7 @@ org.testcontainers testcontainers-bom - 1.16.2 + 1.17.2 pom import @@ -280,6 +275,7 @@ 2.22.2 false + ${project.build.outputDirectory} @@ -361,6 +357,13 @@ org.apache.maven.plugins maven-checkstyle-plugin 3.1.2 + + + com.puppycrawl.tools + checkstyle + 10.3 + + verify-style diff --git a/postgres-testing/pom.xml b/postgres-testing/pom.xml index cca302d6f..3e1bf6828 100644 --- a/postgres-testing/pom.xml +++ b/postgres-testing/pom.xml @@ -5,7 +5,7 @@ org.folio raml-module-builder - 33.2.10-SNAPSHOT + 33.2.11-SNAPSHOT postgres-testing diff --git a/testing/pom.xml b/testing/pom.xml index b9b9a6657..5d3406253 100644 --- a/testing/pom.xml +++ b/testing/pom.xml @@ -5,7 +5,7 @@ org.folio raml-module-builder - 33.2.10-SNAPSHOT + 33.2.11-SNAPSHOT testing diff --git a/util/pom.xml b/util/pom.xml index a99c094ea..ad78cf6ad 100644 --- a/util/pom.xml +++ b/util/pom.xml @@ -5,7 +5,7 @@ org.folio raml-module-builder - 33.2.10-SNAPSHOT + 33.2.11-SNAPSHOT util