JRuby: validate key names and only move _id when necessary.

This commit is contained in:
Kyle Banker 2010-10-07 17:42:39 -04:00
parent 13f49585e9
commit 0c82f01d97
6 changed files with 30 additions and 14 deletions

Binary file not shown.

View File

@ -58,13 +58,18 @@ public class RubyBSONEncoder extends BSONEncoder {
private RubyString _idAsString; private RubyString _idAsString;
private RubyString _tfAsString; private RubyString _tfAsString;
private boolean _check_keys;
private boolean _move_id;
private static final int BIT_SIZE = 64; private static final int BIT_SIZE = 64;
private static final long MAX = (1L << (BIT_SIZE - 1)) - 1; private static final long MAX = (1L << (BIT_SIZE - 1)) - 1;
private static final BigInteger LONG_MAX = BigInteger.valueOf(MAX); private static final BigInteger LONG_MAX = BigInteger.valueOf(MAX);
private static final BigInteger LONG_MIN = BigInteger.valueOf(-MAX - 1); private static final BigInteger LONG_MIN = BigInteger.valueOf(-MAX - 1);
public RubyBSONEncoder(Ruby runtime){ public RubyBSONEncoder(Ruby runtime, boolean check_keys, boolean move_id){
_check_keys = check_keys;
_move_id = move_id;
_runtime = runtime; _runtime = runtime;
_rbclsByteBuffer = _lookupConstant( _runtime, "BSON::ByteBuffer" ); _rbclsByteBuffer = _lookupConstant( _runtime, "BSON::ByteBuffer" );
_rbclsDBRef = _lookupConstant( _runtime, "BSON::DBRef" ); _rbclsDBRef = _lookupConstant( _runtime, "BSON::DBRef" );
@ -140,11 +145,10 @@ public class RubyBSONEncoder extends BSONEncoder {
} }
final int sizePos = _buf.getPosition(); final int sizePos = _buf.getPosition();
_buf.writeInt( 0 ); // leaving space for sthis. set it at the end _buf.writeInt( 0 ); // leaving space for this. set it at the end.
List transientFields = null; List transientFields = null;
boolean rewriteID = ( myType == OBJECT && name == null ); boolean rewriteID = _move_id && ( myType == OBJECT && name == null );
if ( myType == OBJECT ) { if ( myType == OBJECT ) {
@ -175,16 +179,12 @@ public class RubyBSONEncoder extends BSONEncoder {
if( hashKey instanceof String) { if( hashKey instanceof String) {
str = hashKey.toString(); str = hashKey.toString();
} }
else if (hashKey instanceof RubyString) { else if (hashKey instanceof RubyString) {
str = ((RubyString)hashKey).asJavaString(); str = ((RubyString)hashKey).asJavaString();
} }
else if (hashKey instanceof RubySymbol) { else if (hashKey instanceof RubySymbol) {
str = ((RubySymbol)hashKey).asJavaString(); str = ((RubySymbol)hashKey).asJavaString();
} }
testNull(str);
// If we're rewriting the _id, we can move on. // If we're rewriting the _id, we can move on.
if ( rewriteID && str.equals( "_id" ) ) if ( rewriteID && str.equals( "_id" ) )
continue; continue;
@ -208,6 +208,10 @@ public class RubyBSONEncoder extends BSONEncoder {
} }
protected void _putObjectField( String name , Object val ) { protected void _putObjectField( String name , Object val ) {
if( _check_keys )
testValidKey( name );
else
testNull( name );
if ( name.equals( "_transientFields" ) ) if ( name.equals( "_transientFields" ) )
return; return;
@ -395,6 +399,22 @@ public class RubyBSONEncoder extends BSONEncoder {
} }
} }
// Make sure that name contains no null bytes, '.'s
// and doesn't start with a '$'.
private void testValidKey(String str) {
byte[] bytes = str.getBytes();
if( bytes[0] == 36 )
_rbRaise( (RubyClass)_rbclsInvalidKeyName, "$ not allowed in key name.");
for(int j = 0; j < bytes.length; j++ ) {
if(bytes[j] == '\u0000')
_rbRaise( (RubyClass)_rbclsInvalidDocument, "Null not allowed");
if(bytes[j] == 46)
_rbRaise( (RubyClass)_rbclsInvalidKeyName, ". not allowed in key name.");
}
}
private void putIterable( String name , Iterable l ){ private void putIterable( String name , Iterable l ){
_put( ARRAY , name ); _put( ARRAY , name );
final int sizePos = _buf.getPosition(); final int sizePos = _buf.getPosition();

View File

@ -6,7 +6,7 @@ module BSON
# we don't create a new one on each call to #serialize. # we don't create a new one on each call to #serialize.
def self.serialize(obj, check_keys=false, move_id=false) def self.serialize(obj, check_keys=false, move_id=false)
raise InvalidDocument, "BSON_JAVA.serialize takes a Hash" unless obj.is_a?(Hash) raise InvalidDocument, "BSON_JAVA.serialize takes a Hash" unless obj.is_a?(Hash)
enc = Java::OrgJbson::RubyBSONEncoder.new(JRuby.runtime) enc = Java::OrgJbson::RubyBSONEncoder.new(JRuby.runtime, check_keys, move_id)
ByteBuffer.new(enc.encode(obj)) ByteBuffer.new(enc.encode(obj))
end end

View File

@ -484,12 +484,10 @@ class BSONTest < Test::Unit::TestCase
@encoder.serialize(a, false, true).to_s @encoder.serialize(a, false, true).to_s
# Java doesn't support this. Isn't actually necessary. # Java doesn't support this. Isn't actually necessary.
if !(RUBY_PLATFORM =~ /java/)
assert_equal ")\000\000\000\002text\000\004\000\000\000abc\000\002key" + assert_equal ")\000\000\000\002text\000\004\000\000\000abc\000\002key" +
"\000\004\000\000\000abc\000\020_id\000\001\000\000\000\000", "\000\004\000\000\000abc\000\020_id\000\001\000\000\000\000",
@encoder.serialize(a, false, false).to_s @encoder.serialize(a, false, false).to_s
end end
end
def test_move_id_with_nested_doc def test_move_id_with_nested_doc
b = BSON::OrderedHash.new b = BSON::OrderedHash.new

View File

@ -621,7 +621,6 @@ class DBAPITest < Test::Unit::TestCase
assert_equal("mike", @@coll.find_one()["hello"]) assert_equal("mike", @@coll.find_one()["hello"])
end end
if !RUBY_PLATFORM =~ /java/
def test_invalid_key_names def test_invalid_key_names
@@coll.remove @@coll.remove
@ -658,7 +657,6 @@ class DBAPITest < Test::Unit::TestCase
@@coll.insert({"hello" => {"hel.lo" => "world"}}) @@coll.insert({"hello" => {"hel.lo" => "world"}})
end end
end end
end
def test_collection_names def test_collection_names
assert_raise TypeError do assert_raise TypeError do