blob: 87bda2f026b538bff04432aa5f486467130263e1 [file] [log] [blame]
/*******************************************************************************
* 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.olingo.odata2.jpa.processor.core;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.InputStream;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.olingo.odata2.api.edm.Edm;
import org.apache.olingo.odata2.api.edm.EdmException;
import org.apache.olingo.odata2.api.ep.EntityProvider;
import org.apache.olingo.odata2.api.ep.EntityProviderException;
import org.apache.olingo.odata2.api.exception.ODataException;
import org.apache.olingo.odata2.api.exception.ODataMessageException;
import org.apache.olingo.odata2.api.uri.UriParser;
import org.apache.olingo.odata2.api.uri.expression.ExpressionParserException;
import org.apache.olingo.odata2.api.uri.expression.FilterExpression;
import org.apache.olingo.odata2.jpa.processor.api.jpql.JPQLStatement;
import org.junit.BeforeClass;
import org.junit.Test;
public class ODataFilterExpressionParserTest {
private static final short INPUT = 0;
private static final short OUTPUT = 1;
private static final String TABLE_ALIAS = "E1";
private static final String NAMESPACE = "SalesOrderProcessing";
private static final String ENTITY_NOTE = "Note";
// Index 0 - Is test input and Index 1 - Is expected output
private static final String[] EXPRESSION_EQ = { "id eq '123'", "(E1.id LIKE '123' ESCAPE '\\')" };
private static final String[] EXPRESSION_NE = { "id ne '123'", "(E1.id <> '123')" };
private static final String[] EXPRESSION_ESCAPE = { "id ne '123''22'", "(E1.id <> '123''22')" };
private static final String[] EXPRESSION_BINARY_AND =
{
"id le '123' and soId eq 123L and not (substringof(id,'123') eq false) eq true",
"(((E1.id <= '123') AND (E1.soId = 123)) AND (NOT(((CASE WHEN ('123' LIKE CONCAT('%',CONCAT(E1.id,'%')"
+ ") ESCAPE '\\') "
+ "THEN TRUE ELSE FALSE END) = false)) = true))" };
private static final String[] EXPRESSION_BINARY_OR = { "id ge '123' or soId gt 123L",
"((E1.id >= '123') OR (E1.soId > 123))" };
private static final String[] EXPRESSION_MEMBER_OR = { "id lt '123' or oValue/Currency eq 'INR'",
"((E1.id < '123') OR (E1.oValue.Currency LIKE 'INR' ESCAPE '\\'))" };
private static final String[] EXPRESSION_STARTS_WITH = { "startswith(oValue/Currency,'INR')",
"E1.oValue.Currency LIKE CONCAT('INR','%') ESCAPE '\\'" };
private static final String[] EXPRESSION_STARTS_WITH_EQUAL = { "startswith(oValue/Currency,'INR') eq true",
"(E1.oValue.Currency LIKE CONCAT('INR','%') ESCAPE '\\' )" };
private static final String[] EXPRESSION_NOT_STARTS_WITH = { "startswith(oValue/Currency,'INR') eq false",
"(E1.oValue.Currency NOT LIKE CONCAT('INR','%') ESCAPE '\\' )" };
private static final String[] EXPRESSION_NOT_ENDS_WITH = { "endswith(oValue/Currency,tolower('INR')) eq false",
"(E1.oValue.Currency NOT LIKE CONCAT('%',LOWER('INR')) ESCAPE '\\' )" };
private static final String[] EXPRESSION_NESTED_METHOD = {
"endswith(substring(oValue/Currency,2),'INR') eq false",
"(SUBSTRING(E1.oValue.Currency, 2 + 1 ) NOT LIKE CONCAT('%','INR') ESCAPE '\\' )" };
private static final String[] EXPRESSION_SUBSTRING_OF = {
"substringof(id,'123') ne true",
"((CASE WHEN ('123' LIKE CONCAT('%',CONCAT(E1.id,'%')) ESCAPE '\\') THEN TRUE ELSE FALSE END) <> true)" };
private static final String[] EXPRESSION_STARTS_WITH_WRONG_OP = { "startswith(oValue/Currency,'INR') lt true", "" };
private static final String[] EXPRESSION_SUBSTRING_ALL_OP = { "substring(oValue/Currency,1,3) eq 'INR'",
"(SUBSTRING(E1.oValue.Currency, 1 + 1 , 3) LIKE 'INR' ESCAPE '\\')" };
private static final String[] EXPRESSION_SUBSTRINGOF_INJECTION1 = {
"substringof('a'' OR 1=1 OR E1.id LIKE ''b',id) eq true",
"((CASE WHEN (E1.id LIKE CONCAT('%',CONCAT('a'' OR 1=1 OR E1.id LIKE ''b','%')) ESCAPE '\\') "
+ "THEN TRUE ELSE FALSE END) = true)" };
private static final String[] EXPRESSION_SUBSTRINGOF_INJECTION2 =
{
"substringof('substringof(''a'' OR 1=1 OR E1.id LIKE ''b'',id)',id) eq true",
"((CASE WHEN (E1.id LIKE CONCAT('%',CONCAT('substringof(''a'' OR 1=1 OR E1.id LIKE ''b'',id)','%')) ESCAPE '\\') "
+ "THEN TRUE ELSE FALSE END) = true)" };
private static final String[] EXPRESSION_SUBSTRINGOF_INJECTION3 =
{
"substringof( substring(' ) OR execute_my_sql OR '' LIKE ',3),'de''') eq true",
"((CASE WHEN ('de''' LIKE CONCAT('%',CONCAT(SUBSTRING(' ) OR execute_my_sql OR '' LIKE ', 3 + 1 ),'%')"
+ ") ESCAPE '\\') "
+ "THEN TRUE ELSE FALSE END) = true)" };
private static final String[] EXPRESSION_ENDSWITH_INJECTION1 = { "endswith(id,'Str''eet') eq true",
"(E1.id LIKE CONCAT('%','Str''eet') ESCAPE '\\' )" };
private static final String[] EXPRESSION_PRECEDENCE = {
"id eq '123' and id ne '123' or (id eq '123' and id ne '123')",
"(((E1.id LIKE '123' ESCAPE '\\') AND (E1.id <> '123')) OR ((E1.id LIKE '123' ESCAPE '\\') "
+ "AND (E1.id <> '123')))" };
private static final String[] EXPRESSION_DATETIME = { "date eq datetime'2000-01-01T00:00:00'",
"(E1.date = 2000-01-01 00:00:00.000)" };
private static final String[] EXPRESSION_NULL = { "date eq null", "(E1.date IS null)" };
private static final String[] EXPRESSION_NOT_NULL = { "date ne null", "(E1.date IS NOT null)" };
private static final String[] EXPRESSION_STARTSWITH_EQBINARY = { "startswith(id,'123') and text eq 'abc'",
"(E1.id LIKE CONCAT('123','%') ESCAPE '\\' AND (E1.text LIKE 'abc' ESCAPE '\\'))" };
private static final String[] EXPRESSION_STARTSWITHEQ_EQBINARY = { "startswith(id,'123') eq true and text eq 'abc'",
"((E1.id LIKE CONCAT('123','%') ESCAPE '\\' ) AND (E1.text LIKE 'abc' ESCAPE '\\'))" };
private static final String[] EXPRESSION_EQBINARY_STARTSWITH = { "text eq 'abc' and startswith(id,'123')",
"((E1.text LIKE 'abc' ESCAPE '\\') AND E1.id LIKE CONCAT('123','%') ESCAPE '\\')" };
private static final String[] EXPRESSION_EQBINARY_STARTSWITHEQ = { "text eq 'abc' and startswith(id,'123') eq true",
"((E1.text LIKE 'abc' ESCAPE '\\') AND (E1.id LIKE CONCAT('123','%') ESCAPE '\\' ))" };
private static final String[] EXPRESSION_STARTSWITH_STARTSWITH = { "startswith(text,'abc') and startswith(id,'123')",
"(E1.text LIKE CONCAT('abc','%') ESCAPE '\\' AND E1.id LIKE CONCAT('123','%') ESCAPE '\\')" };
private static final String[] EXPRESSION_STARTSWITHEQ_STARTSWITHEQ = {
"startswith(text,'abc') eq true and startswith(id,'123') eq true",
"((E1.text LIKE CONCAT('abc','%') ESCAPE '\\' ) AND (E1.id LIKE CONCAT('123','%') ESCAPE '\\' ))" };
private static final String[] EXPRESSION_STARTSWITH_ANDTRUE = {"startswith(text,'abc') and true",
"(E1.text LIKE CONCAT('abc','%') ESCAPE '\\' AND true)"};
private static final String[] EXPRESSION_STARTSWITHEQTRUE_ANDTRUE = {"startswith(text,'abc') eq true and true",
"((E1.text LIKE CONCAT('abc','%') ESCAPE '\\' ) AND true)"};
private static final String[] EXPRESSION_NULL_EQ = { "id eq null", "(E1.id IS null)" };
private static final String[] EXPRESSION_GUID_EQ = {
"ExternalRecommendationUUID eq guid'56fe79b1-1c88-465b-b309-33bf8b8f6800'",
"(E1.ExternalRecommendationUUID = 56fe79b1-1c88-465b-b309-33bf8b8f6800)" };
private static Edm edm = null;
@BeforeClass
public static void setup() {
InputStream metadataStream =
ODataFilterExpressionParserTest.class.getClassLoader().getResourceAsStream("metadata.xml");
try {
edm = EntityProvider.readMetadata(metadataStream, true);
} catch (EntityProviderException e) {
fail("Not expected");
}
}
@Test
public void testUUID() {
String whereExpression = parseWhereExpression(EXPRESSION_GUID_EQ[INPUT], false);
whereExpression = replacePositionalParameters(whereExpression);
assertEquals(EXPRESSION_GUID_EQ[OUTPUT], whereExpression);
}
@Test
public void testDateTime() {
String whereExpression = parseWhereExpression(EXPRESSION_DATETIME[INPUT], false);
whereExpression = replacePositionalParameters(whereExpression);
assertEquals(EXPRESSION_DATETIME[OUTPUT], whereExpression);
}
@Test
public void testPrecedence() {
String whereExpression = parseWhereExpression(EXPRESSION_PRECEDENCE[INPUT], false);
whereExpression = replacePositionalParameters(whereExpression);
assertEquals(EXPRESSION_PRECEDENCE[OUTPUT], whereExpression);
}
@Test
public void testSubStringOfSQLInjection() {
String whereExpression = parseWhereExpression(EXPRESSION_SUBSTRINGOF_INJECTION1[INPUT], false);
whereExpression = replacePositionalParameters(whereExpression);
assertEquals(EXPRESSION_SUBSTRINGOF_INJECTION1[OUTPUT], whereExpression);
whereExpression = parseWhereExpression(EXPRESSION_SUBSTRINGOF_INJECTION2[INPUT], false);
whereExpression = replacePositionalParameters(whereExpression);
assertEquals(EXPRESSION_SUBSTRINGOF_INJECTION2[OUTPUT], whereExpression);
whereExpression = parseWhereExpression(EXPRESSION_SUBSTRINGOF_INJECTION3[INPUT], false);
whereExpression = replacePositionalParameters(whereExpression);
assertEquals(EXPRESSION_SUBSTRINGOF_INJECTION3[OUTPUT], whereExpression);
}
@Test
public void testEndsWithSQLInjection() {
String whereExpression = parseWhereExpression(EXPRESSION_ENDSWITH_INJECTION1[INPUT], false);
whereExpression = replacePositionalParameters(whereExpression);
assertEquals(EXPRESSION_ENDSWITH_INJECTION1[OUTPUT], whereExpression);
}
@Test
public void testSubStringWithAllOperator() {
String whereExpression = parseWhereExpression(EXPRESSION_SUBSTRING_ALL_OP[INPUT], false);
whereExpression = replacePositionalParameters(whereExpression);
assertEquals(EXPRESSION_SUBSTRING_ALL_OP[OUTPUT], whereExpression);
}
@Test
public void testStartsWithWrongOperator() {
parseWhereExpression(EXPRESSION_STARTS_WITH_WRONG_OP[INPUT], true);
}
@Test
public void testSubStringOf() {
String whereExpression = parseWhereExpression(EXPRESSION_SUBSTRING_OF[INPUT], false);
whereExpression = replacePositionalParameters(whereExpression);
assertEquals(EXPRESSION_SUBSTRING_OF[OUTPUT], whereExpression);
}
@Test
public void testStartsWithEqual() {
String whereExpression = parseWhereExpression(EXPRESSION_STARTS_WITH_EQUAL[INPUT], false);
whereExpression = replacePositionalParameters(whereExpression);
assertEquals(EXPRESSION_STARTS_WITH_EQUAL[OUTPUT], whereExpression);
}
@Test
public void testEscapeCharacters() {
String whereExpression = parseWhereExpression(EXPRESSION_ESCAPE[INPUT], false);
whereExpression = replacePositionalParameters(whereExpression);
assertEquals(EXPRESSION_ESCAPE[OUTPUT], whereExpression);
}
@Test
public void testNotEndsWithToLowerMethod() {
String whereExpression = parseWhereExpression(EXPRESSION_NOT_ENDS_WITH[INPUT], false);
whereExpression = replacePositionalParameters(whereExpression);
assertEquals(EXPRESSION_NOT_ENDS_WITH[OUTPUT], whereExpression);
}
@Test
public void testNestedMethod() {
String whereExpression = parseWhereExpression(EXPRESSION_NESTED_METHOD[INPUT], false);
whereExpression = replacePositionalParameters(whereExpression);
assertEquals(EXPRESSION_NESTED_METHOD[OUTPUT], whereExpression);
}
@Test
public void testNotStartsWith() {
String whereExpression = parseWhereExpression(EXPRESSION_NOT_STARTS_WITH[INPUT], false);
whereExpression = replacePositionalParameters(whereExpression);
assertEquals(EXPRESSION_NOT_STARTS_WITH[OUTPUT], whereExpression);
}
@Test
public void testStartsWith() {
String whereExpression = parseWhereExpression(EXPRESSION_STARTS_WITH[INPUT], false);
whereExpression = replacePositionalParameters(whereExpression);
assertEquals(EXPRESSION_STARTS_WITH[OUTPUT], whereExpression);
}
@Test
public void testSimpleEqRelation() {
String whereExpression = parseWhereExpression(EXPRESSION_EQ[INPUT], false);
whereExpression = replacePositionalParameters(whereExpression);
assertEquals(EXPRESSION_EQ[OUTPUT], whereExpression);
}
@Test
public void testNullEqRelation() {
String whereExpression = parseWhereExpression(EXPRESSION_NULL_EQ[INPUT], false);
whereExpression = replacePositionalParameters(whereExpression);
assertEquals(EXPRESSION_NULL_EQ[OUTPUT], whereExpression);
}
@Test
public void testSimpleNeRelation() {
String whereExpression = parseWhereExpression(EXPRESSION_NE[INPUT], false);
whereExpression = replacePositionalParameters(whereExpression);
assertEquals(EXPRESSION_NE[OUTPUT], whereExpression);
}
@Test
public void testBinaryAnd() {
String whereExpression = parseWhereExpression(EXPRESSION_BINARY_AND[INPUT], false);
whereExpression = replacePositionalParameters(whereExpression);
assertEquals(EXPRESSION_BINARY_AND[OUTPUT], whereExpression);
}
@Test
public void testBinaryOr() {
String whereExpression = parseWhereExpression(EXPRESSION_BINARY_OR[INPUT], false);
whereExpression = replacePositionalParameters(whereExpression);
assertEquals(EXPRESSION_BINARY_OR[OUTPUT], whereExpression);
}
@Test
public void testMemberOr() {
String whereExpression = parseWhereExpression(EXPRESSION_MEMBER_OR[INPUT], false);
whereExpression = replacePositionalParameters(whereExpression);
assertEquals(EXPRESSION_MEMBER_OR[OUTPUT], whereExpression);
}
@Test
public void testNull() {
String whereExpression = parseWhereExpression(EXPRESSION_NULL[INPUT], false);
whereExpression = replacePositionalParameters(whereExpression);
assertEquals(EXPRESSION_NULL[OUTPUT], whereExpression);
}
@Test
public void testNotNull() {
String whereExpression = parseWhereExpression(EXPRESSION_NOT_NULL[INPUT], false);
whereExpression = replacePositionalParameters(whereExpression);
assertEquals(EXPRESSION_NOT_NULL[OUTPUT], whereExpression);
}
private String parseWhereExpression(final String input, final boolean isExceptionExpected) {
FilterExpression expression;
try {
expression = UriParser.parseFilter(edm, edm.getEntityType(NAMESPACE, ENTITY_NOTE), input);
String expressionString = ODataExpressionParser.parseToJPAWhereExpression(expression, TABLE_ALIAS);
return expressionString;
} catch (ExpressionParserException e) {
fail("Not expected");
} catch (EdmException e) {
fail("Not expected");
} catch (ODataMessageException e) {
fail("Not expected");
} catch (ODataException e) {
if (isExceptionExpected) {
assertTrue(true);
} else {
fail("Not expected");
}
}
return "";
}
@Test
public void testStartsWith_BinaryEq() {
String whereExpression = parseWhereExpression(
EXPRESSION_STARTSWITH_EQBINARY[INPUT], false);
whereExpression = replacePositionalParameters(whereExpression);
assertEquals(EXPRESSION_STARTSWITH_EQBINARY[OUTPUT], whereExpression);
}
@Test
public void testBinaryEq_StartsWith() {
String whereExpression = parseWhereExpression(
EXPRESSION_EQBINARY_STARTSWITH[INPUT], false);
whereExpression = replacePositionalParameters(whereExpression);
assertEquals(EXPRESSION_EQBINARY_STARTSWITH[OUTPUT], whereExpression);
}
public void testStartsWithEq_BinaryEq() {
String whereExpression = parseWhereExpression(
EXPRESSION_STARTSWITHEQ_EQBINARY[INPUT], false);
whereExpression = replacePositionalParameters(whereExpression);
assertEquals(EXPRESSION_STARTSWITHEQ_EQBINARY[OUTPUT], whereExpression);
}
@Test
public void testBinaryEq_StartsWithEq() {
String whereExpression = parseWhereExpression(
EXPRESSION_EQBINARY_STARTSWITHEQ[INPUT], false);
whereExpression = replacePositionalParameters(whereExpression);
assertEquals(EXPRESSION_EQBINARY_STARTSWITHEQ[OUTPUT], whereExpression);
}
@Test
public void testStartsWith_StartsWith() {
String whereExpression = parseWhereExpression(
EXPRESSION_STARTSWITH_STARTSWITH[INPUT], false);
whereExpression = replacePositionalParameters(whereExpression);
assertEquals(EXPRESSION_STARTSWITH_STARTSWITH[OUTPUT], whereExpression);
}
@Test
public void testStartsWithEq_StartsWithEq() {
String whereExpression = parseWhereExpression(
EXPRESSION_STARTSWITHEQ_STARTSWITHEQ[INPUT], false);
whereExpression = replacePositionalParameters(whereExpression);
assertEquals(EXPRESSION_STARTSWITHEQ_STARTSWITHEQ[OUTPUT], whereExpression);
}
@Test
public void testStartsWithEq_AndTrue() {
String whereExpression = parseWhereExpression(
EXPRESSION_STARTSWITHEQTRUE_ANDTRUE[INPUT], false);
whereExpression = replacePositionalParameters(whereExpression);
assertEquals(EXPRESSION_STARTSWITHEQTRUE_ANDTRUE[OUTPUT], whereExpression);
}
@Test
public void testStarts_AndTrue() {
String whereExpression = parseWhereExpression(
EXPRESSION_STARTSWITH_ANDTRUE[INPUT], false);
whereExpression = replacePositionalParameters(whereExpression);
assertEquals(EXPRESSION_STARTSWITH_ANDTRUE[OUTPUT], whereExpression);
}
private String replacePositionalParameters(String whereExpression) {
Map<Integer, Object> positionalParameters = ODataExpressionParser.getPositionalParametersThreadLocal();
for (Entry<Integer, Object> param : positionalParameters.entrySet()) {
Integer key = param.getKey();
if (param.getValue() instanceof String) {
whereExpression = whereExpression.replaceAll("\\?" + String.valueOf(key), "\'" + param.getValue() + "\'");
} else if (param.getValue() instanceof Timestamp || param.getValue() instanceof Calendar){
Calendar datetime = (Calendar) param.getValue();
String year = String.format("%04d", datetime.get(Calendar.YEAR));
String month = String.format("%02d", datetime.get(Calendar.MONTH) + 1);
String day = String.format("%02d", datetime.get(Calendar.DAY_OF_MONTH));
String hour = String.format("%02d", datetime.get(Calendar.HOUR_OF_DAY));
String min = String.format("%02d", datetime.get(Calendar.MINUTE));
String sec = String.format("%02d", datetime.get(Calendar.SECOND));
String value =
year + JPQLStatement.DELIMITER.HYPHEN + month + JPQLStatement.DELIMITER.HYPHEN + day
+ JPQLStatement.DELIMITER.SPACE + hour + JPQLStatement.DELIMITER.COLON + min
+ JPQLStatement.DELIMITER.COLON + sec + JPQLStatement.KEYWORD.OFFSET;
whereExpression = whereExpression.replaceAll("\\?" + String.valueOf(key), value);
} else if(param.getValue() instanceof Byte[]){
byte[] byteValue = convertToByte((Byte[])param.getValue());
whereExpression = whereExpression.replaceAll("\\?" + String.valueOf(key), new String(byteValue));
}else {
whereExpression = whereExpression.replaceAll("\\?" + String.valueOf(key), param.getValue().toString());
}
}
return whereExpression;
}
private byte[] convertToByte(Byte[] value) {
int length = value.length;
if (length == 0) {
return new byte[0];
}
final byte[] result = new byte[length];
for (int i = 0; i < length; i++) {
result[i] = value[i];
}
return result;
}
}