Skip to content

Commit 080057a

Browse files
FINERACT-2535: Support BOOLEAN in runreports endpoint
1 parent 2454f9d commit 080057a

3 files changed

Lines changed: 113 additions & 8 deletions

File tree

fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/database/JdbcJavaType.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public Object toJdbcValueImpl(@NonNull DatabaseType dialect, Object value) {
3434
return value == null ? null : (Boolean.TRUE.equals(value) ? 1 : 0);
3535
}
3636
},
37-
BOOLEAN(JavaType.BOOLEAN, new DialectType(JDBCType.BIT), new DialectType(JDBCType.BOOLEAN, null, "BOOL")) { //
37+
BOOLEAN(JavaType.BOOLEAN, new DialectType(JDBCType.BIT, null, "BOOL", "BOOLEAN"), new DialectType(JDBCType.BOOLEAN, null, "BOOL")) { //
3838

3939
@Override
4040
public Object toJdbcValueImpl(@NonNull DatabaseType dialect, Object value) {
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.fineract.infrastructure.core.service.database;
20+
21+
import static org.junit.jupiter.api.Assertions.assertEquals;
22+
23+
import org.junit.jupiter.api.Test;
24+
25+
class JdbcJavaTypeTest {
26+
27+
@Test
28+
void shouldResolveBooleanTypeNameForMySql() {
29+
JdbcJavaType jdbcJavaType = JdbcJavaType.getByTypeName(DatabaseType.MYSQL, "BOOLEAN", true);
30+
assertEquals(JdbcJavaType.BOOLEAN, jdbcJavaType);
31+
}
32+
33+
@Test
34+
void shouldResolveBoolTypeNameForMySql() {
35+
JdbcJavaType jdbcJavaType = JdbcJavaType.getByTypeName(DatabaseType.MYSQL, "BOOL", true);
36+
assertEquals(JdbcJavaType.BOOLEAN, jdbcJavaType);
37+
}
38+
39+
@Test
40+
void shouldResolveBitTypeNameForMySql() {
41+
JdbcJavaType jdbcJavaType = JdbcJavaType.getByTypeName(DatabaseType.MYSQL, "BIT", true);
42+
assertEquals(JdbcJavaType.BOOLEAN, jdbcJavaType);
43+
}
44+
}

integration-tests/src/test/java/org/apache/fineract/integrationtests/SqlInjectionReportingServiceIntegrationTest.java

Lines changed: 68 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,11 @@ public class SqlInjectionReportingServiceIntegrationTest extends BaseLoanIntegra
6767
private RequestSpecification requestSpec;
6868
private ResponseSpecification responseSpec;
6969
private Long testReportId = null;
70+
private Long booleanReportId = null;
7071
private static final String TEST_REPORT_NAME = "SQL_Injection_Test_Report";
7172
private static final String TEST_REPORT_SQL = "SELECT 1 as test_column, 'Test Data' as test_name";
73+
private static final String BOOLEAN_REPORT_SQL = "SELECT CAST(1 AS BOOLEAN) AS active";
74+
private String booleanReportName;
7275

7376
@BeforeEach
7477
public void setup() {
@@ -92,6 +95,13 @@ public void cleanup() {
9295
log.warn("Failed to clean up test report: " + e.getMessage());
9396
}
9497
}
98+
if (booleanReportId != null) {
99+
try {
100+
deleteBooleanReport();
101+
} catch (Exception e) {
102+
log.warn("Failed to clean up boolean test report: " + e.getMessage());
103+
}
104+
}
95105
}
96106

97107
private void createTestReportIfNotExists() {
@@ -161,6 +171,31 @@ private void createTestReportIfNotExists() {
161171
}
162172
}
163173

174+
private void createBooleanReport() {
175+
booleanReportName = "BOOLEAN_Runreports_Test_Report_" + System.currentTimeMillis();
176+
177+
String reportJson = "{" + "\"reportName\": \"" + booleanReportName + "\"," + "\"reportType\": \"Table\","
178+
+ "\"reportCategory\": \"Client\"," + "\"reportSql\": \"" + BOOLEAN_REPORT_SQL + "\","
179+
+ "\"description\": \"Test report for BOOLEAN runreports support\"," + "\"useReport\": true" + "}";
180+
181+
Response postResponse = given().spec(requestSpec).contentType(ContentType.JSON).body(reportJson).when()
182+
.post("/fineract-provider/api/v1/reports");
183+
184+
if (postResponse.getStatusCode() == 200 || postResponse.getStatusCode() == 201) {
185+
String response = postResponse.asString();
186+
if (response.contains("resourceId")) {
187+
String idStr = response.replaceAll(".*\"resourceId\":(\\d+).*", "$1");
188+
booleanReportId = Long.parseLong(idStr);
189+
log.info("Created BOOLEAN test report with ID: {}", booleanReportId);
190+
} else {
191+
throw new RuntimeException("BOOLEAN test report creation failed - no resourceId in response: " + response);
192+
}
193+
} else {
194+
throw new RuntimeException(
195+
"BOOLEAN test report creation failed with status " + postResponse.getStatusCode() + ": " + postResponse.asString());
196+
}
197+
}
198+
164199
private void deleteTestReport() {
165200
if (testReportId != null) {
166201
try {
@@ -172,6 +207,17 @@ private void deleteTestReport() {
172207
}
173208
}
174209

210+
private void deleteBooleanReport() {
211+
if (booleanReportId != null) {
212+
try {
213+
Utils.performServerDelete(requestSpec, responseSpec, "/fineract-provider/api/v1/reports/" + booleanReportId, "");
214+
log.info("Deleted BOOLEAN test report with ID: {}", booleanReportId);
215+
} catch (Exception e) {
216+
log.warn("Failed to delete BOOLEAN test report: " + e.getMessage());
217+
}
218+
}
219+
}
220+
175221
/**
176222
* UC1: Test legitimate report execution works correctly Validates that the SQL injection prevention doesn't break
177223
* normal functionality
@@ -215,7 +261,7 @@ void uc2_testParameterInjectionPrevention() {
215261
// This should either succeed with empty/safe results or fail with validation error
216262
// but NOT with SQL syntax errors
217263
try {
218-
String response = Utils.performServerGet(requestSpec, responseSpec, "/fineract-provider/api/v1/runreports/" + TEST_REPORT_NAME
264+
Utils.performServerGet(requestSpec, responseSpec, "/fineract-provider/api/v1/runreports/" + TEST_REPORT_NAME
219265
+ "?genericResultSet=false&" + toQueryString(maliciousParams), null);
220266

221267
// If we get here, the SQL injection was prevented and handled safely
@@ -240,7 +286,7 @@ void uc2_testParameterInjectionPrevention() {
240286
* whitelist implementation for report types
241287
*/
242288
@ParameterizedTest(name = "Report Type Validation: {0}")
243-
@ValueSource(strings = { "report", "parameter" })
289+
@ValueSource(strings = {"report", "parameter"})
244290
void uc3_testValidReportTypes(String validType) {
245291
log.info("Testing valid report type: {}", validType);
246292

@@ -249,7 +295,7 @@ void uc3_testValidReportTypes(String validType) {
249295

250296
// Test that valid report types work through the API
251297
try {
252-
String response = Utils.performServerGet(requestSpec, responseSpec,
298+
Utils.performServerGet(requestSpec, responseSpec,
253299
"/runreports/TestReport?reportType=" + validType + "&genericResultSet=false&" + toQueryString(queryParams), null);
254300
// Should get a proper response or 404 (report not found), not validation error
255301
} catch (AssertionError e) {
@@ -262,7 +308,7 @@ void uc3_testValidReportTypes(String validType) {
262308
* UC4: Test invalid report types that should be rejected by whitelist
263309
*/
264310
@ParameterizedTest(name = "Invalid Report Type: {0}")
265-
@ValueSource(strings = { "table", "view", "procedure", "function", "schema", "database", "admin", "user", "system", "config" })
311+
@ValueSource(strings = {"table", "view", "procedure", "function", "schema", "database", "admin", "user", "system", "config"})
266312
void uc4_testInvalidReportTypes(String invalidType) {
267313
log.info("Testing invalid report type: {}", invalidType);
268314

@@ -438,8 +484,8 @@ void uc8_testComplexParameterInjection() {
438484
* UC9: Test legitimate reports with various parameter types
439485
*/
440486
@ParameterizedTest(name = "Parameter Type: {0}")
441-
@CsvSource(delimiterString = " | ", value = { "R_officeId | 1 | Numeric parameter", "R_startDate | 2023-01-01 | Date parameter",
442-
"R_endDate | 2023-12-31 | Date parameter", "R_currencyId | USD | String parameter", "R_loanProductId | 1 | Numeric parameter" })
487+
@CsvSource(delimiterString = " | ", value = {"R_officeId | 1 | Numeric parameter", "R_startDate | 2023-01-01 | Date parameter",
488+
"R_endDate | 2023-12-31 | Date parameter", "R_currencyId | USD | String parameter", "R_loanProductId | 1 | Numeric parameter"})
443489
void uc9_testLegitimateParameterTypes(String paramName, String paramValue, String description) {
444490
log.info("Testing legitimate parameter: {} = {} ({})", paramName, paramValue, description);
445491

@@ -496,6 +542,21 @@ void uc10_testCrossDatabaseCompatibility() {
496542
}
497543
}
498544

545+
@Test
546+
void shouldExecuteReportSuccessfullyWhenReportContainsBooleanColumn() {
547+
createBooleanReport();
548+
549+
String response = Utils.performServerGet(requestSpec, responseSpec,
550+
"/fineract-provider/api/v1/runreports/" + URLEncoder.encode(booleanReportName, StandardCharsets.UTF_8)
551+
+ "?genericResultSet=false",
552+
null);
553+
554+
assertNotNull(response);
555+
assertFalse(response.isBlank());
556+
assertFalse(response.contains("Data type 'BOOLEAN' is not supported"));
557+
assertTrue(response.contains("columnHeaders") || response.contains("data"));
558+
}
559+
499560
/**
500561
* Helper method to convert parameters map to query string
501562
*/
@@ -512,4 +573,4 @@ private String toQueryString(Map<String, String> params) {
512573
}
513574
return sb.toString();
514575
}
515-
}
576+
}

0 commit comments

Comments
 (0)