Skip to content

Commit

Permalink
Improved RQL API and implementation, protected against RQL injection …
Browse files Browse the repository at this point in the history
…attacks.
  • Loading branch information
nmihajlovski committed Mar 5, 2015
1 parent fc32cdb commit c013a32
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 22 deletions.
Expand Up @@ -113,19 +113,33 @@ public <E> Class<E> getEntityTypeFor(Class<E> clazz) {
}

@Override
public Object entity(String data) {
String entityName = U.capitalized(data.split(" ")[0]);
public Object entity(String rql, Object... args) {

String[] parts = rql.split(" ");

String entityName = U.capitalized(parts[0]);
Class<?> entityType = getEntityType(entityName);
U.must(entityType != null, "Cannot find entity '%s'!", entityName);

String[] props = data.substring(entityName.length() + 1).split("\\s*\\,\\s*");
Map<String, Object> properties = U.map();
if (parts.length > 1) {

String[] props = rql.substring(entityName.length() + 1).split("\\s*\\,\\s*");

int argIndex = 0;
for (String prop : props) {
String[] kv = prop.trim().split("\\s*=\\s*");
String key = kv[0];
Object value;

for (String prop : props) {
String[] kv = prop.trim().split("\\s*=\\s*");
String key = kv[0];
Object value = kv.length > 1 ? kv[1] : true;
properties.put(key, value);
if (kv.length > 1) {
value = kv[1].equals("?") ? args[argIndex++] : kv[1];
} else {
value = true;
}

properties.put(key, value);
}
}

return entity(entityType, properties);
Expand Down
Expand Up @@ -313,21 +313,22 @@ public void init(String data, Object... args) {

@SuppressWarnings("unchecked")
@Override
public <T> T entity(String data, Object... args) {
data = U.format(data, args);
return (T) schema().entity(data);
public <E> E entity(String rql, Object... args) {
return (E) schema().entity(rql, args);
}

@SuppressWarnings("unchecked")
@Override
public void rql(String rql, Object... args) {
rql = rql.trim();
public <RESULT> RESULT rql(String rql, Object... args) {

int p = rql.indexOf(' ');
U.must(p > 0, "Invalid RQL syntax!");

String cmd = rql.substring(0, p).trim();
String data = rql.substring(p + 1).trim();
U.show(cmd, data, args);

if (cmd.equalsIgnoreCase("INSERT")) {
insert(entity(data, args));
return (RESULT) new Long(insert(entity(data, args)));
} else {
throw U.rte("Unknown RQL command: '%s'!", cmd);
}
Expand Down
106 changes: 106 additions & 0 deletions rapidoid-db-tests/src/test/java/org/rapidoid/db/RQLTest.java
@@ -0,0 +1,106 @@
package org.rapidoid.db;

/*
* #%L
* rapidoid-db-tests
* %%
* Copyright (C) 2014 - 2015 Nikolche Mihajlovski
* %%
* 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.
* #L%
*/

import org.rapidoid.annotation.Authors;
import org.rapidoid.annotation.DbEntity;
import org.rapidoid.annotation.Since;
import org.testng.annotations.Test;

@DbEntity
class Abc {
long id;
String name = "a";
int n = 55;
boolean z = false;
LowHigh lh;
}

enum LowHigh {
LOW, HIGH
}

@Authors("Nikolche Mihajlovski")
@Since("2.3.0")
public class RQLTest extends DbTestCommons {

@Test
public void testRQLEntityConstruction() {
Abc x = DB.entity("abc name=n1, n=123, z, lh=high");
eq(x.name, "n1");
eq(x.n, 123);
isTrue(x.z);
eq(x.lh, LowHigh.HIGH);
}

@Test
public void testRQLEntityConstructionDefaults() {
Abc x = DB.entity("Abc");
eq(x.name, "a");
eq(x.n, 55);
isFalse(x.z);
eq(x.lh, null);
}

@Test
public void testRQLParameterizedEntityConstruction() {
Abc x = DB.entity("abc name=?, n=?, z, lh=?", "n1", 123, LowHigh.HIGH);
eq(x.name, "n1");
eq(x.n, 123);
isTrue(x.z);
eq(x.lh, LowHigh.HIGH);
}

@Test
public void testRQLInjection() {
// RQL special characters
String strange = "thename, n=789, z=?, ? ? , ?,,";

Abc x = DB.entity("abc name=?, z, lh=?", strange, LowHigh.HIGH);
eq(x.name, strange);
eq(x.n, 55);
isTrue(x.z);
eq(x.lh, LowHigh.HIGH);
}

@Test
public void testRQLInsert() {
long id = DB.rql("INSERT Abc name=n1, n=123, z=false, lh=low");
Abc x = DB.get(id);

eq(x.name, "n1");
eq(x.n, 123);
isFalse(x.z);
eq(x.lh, LowHigh.LOW);
}

@Test
public void testRQLParameterizedInsert() {
long id = DB.rql("INSERT Abc name=?, n=123, z=?, lh=low", "n1", false);
Abc x = DB.get(id);

eq(x.name, "n1");
eq(x.n, 123);
isFalse(x.z);
eq(x.lh, LowHigh.LOW);
}

}
8 changes: 4 additions & 4 deletions rapidoid-db/src/main/java/org/rapidoid/db/DB.java
Expand Up @@ -280,12 +280,12 @@ public static void init(String data, Object... args) {
db().init(data, args);
}

public static void rql(String rql, Object... args) {
db().rql(rql, args);
public static <RESULT> RESULT rql(String rql, Object... args) {
return db().rql(rql, args);
}

public static <T> T entity(String data, Object... args) {
return db().entity(data, args);
public static <E> E entity(String rql, Object... args) {
return db().entity(rql, args);
}

}
4 changes: 2 additions & 2 deletions rapidoid-db/src/main/java/org/rapidoid/db/Database.java
Expand Up @@ -116,8 +116,8 @@ public interface Database extends Activity<Database> {

void init(String data, Object... args);

void rql(String rql, Object... args);
<RESULT> RESULT rql(String rql, Object... args);

<T> T entity(String data, Object... args);
<E> E entity(String rql, Object... args);

}
2 changes: 1 addition & 1 deletion rapidoid-db/src/main/java/org/rapidoid/db/DbSchema.java
Expand Up @@ -39,6 +39,6 @@ public interface DbSchema {

<E> Class<E> getEntityTypeFor(Class<E> clazz);

Object entity(String data);
Object entity(String rql, Object... args);

}

0 comments on commit c013a32

Please sign in to comment.