Skip to content

Commit

Permalink
Refactor address query to use address table
Browse files Browse the repository at this point in the history
Add dummy entity to give EntityManagerFactory a package folder to load entities. This is a workaround to prevent an error described here: spring-projects/spring-boot#6635
Extract string fields into constants
Add needed property files for OWASP library
Removed example migration sql
  • Loading branch information
crain committed Jul 7, 2017
1 parent 73acc99 commit f93d9e6
Show file tree
Hide file tree
Showing 8 changed files with 629 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

Expand All @@ -43,6 +44,7 @@
@EnableMariaDB
@EnableAnubis
@EnableServiceException
@EnableJpaRepositories(basePackages = { "io.mifos.reporting.service.internal.repository" })
@ComponentScan({
"io.mifos.reporting.service.rest",
"io.mifos.reporting.service.internal"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright 2016 The Mifos Initiative.
*
* Licensed 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 io.mifos.reporting.service.internal.repository;

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class DummyEntity {

@Id
private Long id;

public DummyEntity() {
super();
}

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright 2016 The Mifos Initiative.
*
* Licensed 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 io.mifos.reporting.service.internal.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface DummyRepository extends JpaRepository<DummyEntity, Long> {
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,22 @@
@Report(category = "Customer", identifier = "Listing")
public class CustomerListReportSpecification implements ReportSpecification {

private static final String DATE_RANGE = "Date range";
private static final String STATE = "State";
private static final String CUSTOMER = "Customer";
private static final String FIRST_NAME = "First name";
private static final String MIDDLE_NAME = "Middle name";
private static final String LAST_NAME = "Last name";
private static final String ACCOUNT_NUMBER = "Account number";
private static final String ADDRESS = "Address";

private final Logger logger;

private final EntityManager entityManager;
private final HashMap<String, String> customerColumnMapping = new HashMap<>();
private final HashMap<String, String> addressColumnMapping = new HashMap<>();
private final HashMap<String, String> accountColumnMapping = new HashMap<>();
private final HashMap<String, String> allColumnMapping = new HashMap<>();

@Autowired
public CustomerListReportSpecification(@Qualifier(ServiceConstants.LOGGER_NAME) final Logger logger,
Expand Down Expand Up @@ -104,13 +115,13 @@ public ReportPage generateReport(final ReportRequest reportRequest, final int pa
public void validate(final ReportRequest reportRequest) throws IllegalArgumentException {
final ArrayList<String> unknownFields = new ArrayList<>();
reportRequest.getQueryParameters().forEach(queryParameter -> {
if (!this.customerColumnMapping.keySet().contains(queryParameter.getName())) {
if (!this.allColumnMapping.keySet().contains(queryParameter.getName())) {
unknownFields.add(queryParameter.getName());
}
});

reportRequest.getDisplayableFields().forEach(displayableField -> {
if (!this.customerColumnMapping.keySet().contains(displayableField.getName())) {
if (!this.allColumnMapping.keySet().contains(displayableField.getName())) {
unknownFields.add(displayableField.getName());
}
});
Expand All @@ -123,16 +134,20 @@ public void validate(final ReportRequest reportRequest) throws IllegalArgumentEx
}

private void initializeMapping() {
this.customerColumnMapping.put("Date Range", "cst.created_on");
this.customerColumnMapping.put("State", "cst.current_state");
this.customerColumnMapping.put("Customer", "cst.identifier");
this.customerColumnMapping.put("First name", "cst.given_name");
this.customerColumnMapping.put("Middle Name", "cst.middle_name");
this.customerColumnMapping.put("Last Name", "cst.surname");
this.customerColumnMapping.put(DATE_RANGE, "cst.created_on");
this.customerColumnMapping.put(STATE, "cst.current_state");
this.customerColumnMapping.put(CUSTOMER, "cst.identifier");
this.customerColumnMapping.put(FIRST_NAME, "cst.given_name");
this.customerColumnMapping.put(MIDDLE_NAME, "cst.middle_name");
this.customerColumnMapping.put(LAST_NAME, "cst.surname");

this.accountColumnMapping.put("Account number", "acc.identifier, acc.balance");
this.accountColumnMapping.put(ACCOUNT_NUMBER, "acc.identifier, acc.balance");

this.addressColumnMapping.put("Address", "CONCAT(adr.street, ', ', adr.postal_code, ', ', adr.city)");
this.addressColumnMapping.put(ADDRESS, "CONCAT(adr.street, ', ', adr.postal_code, ', ', adr.city)");

this.allColumnMapping.putAll(customerColumnMapping);
this.allColumnMapping.putAll(accountColumnMapping);
this.allColumnMapping.putAll(addressColumnMapping);
}

private Header createHeader(final List<DisplayableField> displayableFields) {
Expand All @@ -152,15 +167,34 @@ private List<Row> buildRows(final ReportRequest reportRequest, final List<?> cus
customerResultList.forEach(result -> {
final Row row = new Row();
row.setValues(new ArrayList<>());
final Object[] resultValues = (Object[]) result;
for (final Object resultValue : resultValues) {

final String customerIdentifier;

if (result instanceof Object[]) {
final Object[] resultValues = (Object[]) result;

customerIdentifier = resultValues[0].toString();

for (final Object resultValue : resultValues) {
final Value value = new Value();
if (resultValue != null) {
value.setValues(new String[]{resultValue.toString()});
} else {
value.setValues(new String[]{});
}

row.getValues().add(value);
}
} else {
customerIdentifier = result.toString();

final Value value = new Value();
value.setValues(new String[]{resultValue.toString()});
value.setValues(new String[]{result.toString()});
row.getValues().add(value);
}

final DecimalFormat decimalFormat = new DecimalFormat(".##");
final Query accountQuery = this.entityManager.createNativeQuery(this.buildAccountQuery(reportRequest, resultValues[0].toString()));
final DecimalFormat decimalFormat = new DecimalFormat("0.##");
final Query accountQuery = this.entityManager.createNativeQuery(this.buildAccountQuery(reportRequest, customerIdentifier));
final List<?> accountResultList = accountQuery.getResultList();
final ArrayList<String> values = new ArrayList<>();
accountResultList.forEach(accountResult -> {
Expand All @@ -175,7 +209,7 @@ private List<Row> buildRows(final ReportRequest reportRequest, final List<?> cus
accountValue.setValues(values.toArray(new String[values.size()]));
row.getValues().add(accountValue);

final String addressQueryString = this.buildAddressQuery(reportRequest, resultValues[0].toString());
final String addressQueryString = this.buildAddressQuery(reportRequest, customerIdentifier);
if (addressQueryString != null) {
final Query addressQuery = this.entityManager.createNativeQuery(addressQueryString);
final List<?> resultList = addressQuery.getResultList();
Expand All @@ -192,19 +226,19 @@ private List<Row> buildRows(final ReportRequest reportRequest, final List<?> cus

private List<QueryParameter> buildQueryParameters() {
return Arrays.asList(
QueryParameterBuilder.create("Date range", Type.DATE).operator(QueryParameter.Operator.BETWEEN).build(),
QueryParameterBuilder.create("State", Type.TEXT).operator(QueryParameter.Operator.IN).build()
QueryParameterBuilder.create(DATE_RANGE, Type.DATE).operator(QueryParameter.Operator.BETWEEN).build(),
QueryParameterBuilder.create(STATE, Type.TEXT).operator(QueryParameter.Operator.IN).build()
);
}

private List<DisplayableField> buildDisplayableFields() {
return Arrays.asList(
DisplayableFieldBuilder.create("Customer", Type.TEXT).mandatory().build(),
DisplayableFieldBuilder.create("First name", Type.TEXT).build(),
DisplayableFieldBuilder.create("Middle name", Type.TEXT).build(),
DisplayableFieldBuilder.create("Last name", Type.TEXT).build(),
DisplayableFieldBuilder.create("Account number", Type.TEXT).mandatory().build(),
DisplayableFieldBuilder.create("Address", Type.TEXT).build()
DisplayableFieldBuilder.create(CUSTOMER, Type.TEXT).mandatory().build(),
DisplayableFieldBuilder.create(FIRST_NAME, Type.TEXT).build(),
DisplayableFieldBuilder.create(MIDDLE_NAME, Type.TEXT).build(),
DisplayableFieldBuilder.create(LAST_NAME, Type.TEXT).build(),
DisplayableFieldBuilder.create(ACCOUNT_NUMBER, Type.TEXT).mandatory().build(),
DisplayableFieldBuilder.create(ADDRESS, Type.TEXT).build()
);
}

Expand All @@ -228,9 +262,14 @@ private String buildCustomerQuery(final ReportRequest reportRequest, int pageInd
if (!queryParameters.isEmpty()) {
query.append(" WHERE ");
final ArrayList<String> criteria = new ArrayList<>();
queryParameters.forEach(queryParameter -> criteria.add(
CriteriaBuilder.buildCriteria(this.customerColumnMapping.get(queryParameter.getName()), queryParameter))
);
queryParameters.forEach(queryParameter -> {
if(queryParameter.getValue() != null && !queryParameter.getValue().isEmpty()) {
criteria.add(
CriteriaBuilder.buildCriteria(this.customerColumnMapping.get(queryParameter.getName()), queryParameter)
);
}
});

query.append(criteria.stream().collect(Collectors.joining(" AND ")));
}
query.append(" ORDER BY cst.identifier");
Expand Down Expand Up @@ -275,10 +314,9 @@ private String buildAddressQuery(final ReportRequest reportRequest, final String

if (!columns.isEmpty()) {
return "SELECT " + columns.stream().collect(Collectors.joining(", ")) + " " +
"FROM thoth_accounts acc " +
"LEFT JOIN maat_customers cst on acc.holders = cst.identifier " +
"WHERE cst.identifier ='" + customerIdentifier + "' " +
"ORDER BY acc.identifier";
"FROM maat_addresses adr " +
"LEFT JOIN maat_customers cst on adr.id = cst.address_id " +
"WHERE cst.identifier ='" + customerIdentifier + "' ";
}
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,18 @@
public class CriteriaBuilder {

// https://www.owasp.org/index.php/SQL_Injection_Prevention_Cheat_Sheet
private static final Encoder ENCODER = ESAPI.encoder();
private static final MySQLCodec MY_SQL_CODEC = new MySQLCodec(MySQLCodec.Mode.ANSI);
public static Encoder ENCODER;
public static MySQLCodec MY_SQL_CODEC;

static {
// TODO move this code into bean
try {
ENCODER = ESAPI.encoder();
MY_SQL_CODEC = new MySQLCodec(MySQLCodec.Mode.ANSI);
} catch(final Exception e) {
System.out.println(e.getMessage());
}
}

private CriteriaBuilder() {
super();
Expand Down Expand Up @@ -67,6 +77,7 @@ public static String buildCriteria(final String field, final QueryParameter quer
.map(s -> "'" + CriteriaBuilder.ENCODER.encodeForSQL(CriteriaBuilder.MY_SQL_CODEC, s) + "'")
.collect(Collectors.joining(","))
);
criteria.append(")");
break;
case BETWEEN:
final String[] splitString = queryParameter.getValue().split("\\.\\.");
Expand Down

0 comments on commit f93d9e6

Please sign in to comment.