Skip to content

Commit 2a1858c

Browse files
committed
JAVA-1644: Check validity of database name and collection name
Add static helper methods to MongoNamespace for checking validity prior to construction Check validity of database name and collection name in high-level API
1 parent 1354680 commit 2a1858c

File tree

15 files changed

+158
-46
lines changed

15 files changed

+158
-46
lines changed

driver-async/src/main/com/mongodb/async/client/MongoClient.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ public interface MongoClient extends Closeable {
3737
*
3838
* @param name the name of the database
3939
* @return the database
40+
* @throws IllegalArgumentException if databaseName is invalid
41+
* @see com.mongodb.MongoNamespace#checkDatabaseNameValidity(String)
4042
*/
4143
MongoDatabase getDatabase(String name);
4244

driver-async/src/main/com/mongodb/async/client/MongoDatabase.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ public interface MongoDatabase {
117117
*
118118
* @param collectionName the name of the collection to return
119119
* @return the collection
120+
* @throws IllegalArgumentException if collectionName is invalid
121+
* @see com.mongodb.MongoNamespace#checkCollectionNameValidity(String)
120122
*/
121123
MongoCollection<Document> getCollection(String collectionName);
122124

driver-async/src/main/com/mongodb/async/client/MongoDatabaseImpl.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import java.util.ArrayList;
4040
import java.util.List;
4141

42+
import static com.mongodb.MongoNamespace.checkDatabaseNameValidity;
4243
import static com.mongodb.assertions.Assertions.notNull;
4344

4445
class MongoDatabaseImpl implements MongoDatabase {
@@ -51,6 +52,7 @@ class MongoDatabaseImpl implements MongoDatabase {
5152

5253
MongoDatabaseImpl(final String name, final CodecRegistry codecRegistry, final ReadPreference readPreference,
5354
final WriteConcern writeConcern, final ReadConcern readConcern, final AsyncOperationExecutor executor) {
55+
checkDatabaseNameValidity(name);
5456
this.name = notNull("name", name);
5557
this.codecRegistry = notNull("codecRegistry", codecRegistry);
5658
this.readPreference = notNull("readPreference", readPreference);

driver-async/src/test/unit/com/mongodb/async/client/MongoDatabaseSpecification.groovy

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,25 @@ class MongoDatabaseSpecification extends Specification {
5757
def readConcern = ReadConcern.DEFAULT
5858
def collation = Collation.builder().locale('en').build()
5959

60+
def 'should throw IllegalArgumentException if name is invalid'() {
61+
when:
62+
new MongoDatabaseImpl('a.b', codecRegistry, readPreference, writeConcern, readConcern, new TestOperationExecutor([]))
63+
64+
then:
65+
thrown(IllegalArgumentException)
66+
}
67+
68+
def 'should throw IllegalArgumentException from getCollection if collectionName is invalid'() {
69+
given:
70+
def database = new MongoDatabaseImpl(name, codecRegistry, readPreference, writeConcern, readConcern, new TestOperationExecutor([]))
71+
72+
when:
73+
database.getCollection('')
74+
75+
then:
76+
thrown(IllegalArgumentException)
77+
}
78+
6079
def 'should return the correct name from getName'() {
6180
given:
6281
def database = new MongoDatabaseImpl(name, codecRegistry, readPreference, writeConcern, readConcern, new TestOperationExecutor([]))

driver-core/src/main/com/mongodb/MongoNamespace.java

Lines changed: 64 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import static com.mongodb.assertions.Assertions.isTrueArgument;
2222
import static com.mongodb.assertions.Assertions.notNull;
23+
import static java.util.Arrays.asList;
2324

2425
/**
2526
* A MongoDB namespace, which includes a database name and collection name.
@@ -35,30 +36,66 @@ public final class MongoNamespace {
3536
private final String fullName; // cache to avoid repeated string building
3637

3738
/**
38-
* Construct an instance.
39+
* Check the validity of the given database name. A valid database name is non-null, non-empty, and does not contain any of the
40+
* following strings: {@code " ", "."}. The server may impose additional restrictions on database names.
3941
*
40-
* @param fullName the full namespace
42+
* @param databaseName the database name
43+
* @throws IllegalArgumentException if the database name is invalid
44+
* @since 3.4
45+
* @mongodb.driver.manual reference/limits/#naming-restrictions Naming Restrictions
4146
*/
42-
public MongoNamespace(final String fullName) {
43-
notNull("fullName", fullName);
44-
isTrueArgument("fullName is of form <db>.<collection>", isFullNameValid(fullName));
47+
public static void checkDatabaseNameValidity(final String databaseName) {
48+
notNull("databaseName", databaseName);
49+
isTrueArgument("databaseName is not empty", !databaseName.isEmpty());
50+
for (String cur : asList(" ", ".")) {
51+
isTrueArgument("databaseName does not contain '" + cur + "'", !databaseName.contains(cur));
52+
}
53+
}
4554

55+
/**
56+
* Check the validity of the given collection name. A valid collection name is non-null and non-empty. The server may impose
57+
* additional restrictions on collection names.
58+
*
59+
* @param collectionName the collection name
60+
* @throws IllegalArgumentException if the collection name is invalid
61+
* @since 3.4
62+
* @mongodb.driver.manual reference/limits/#naming-restrictions Naming Restrictions
63+
*/
64+
public static void checkCollectionNameValidity(final String collectionName) {
65+
notNull("collectionName", collectionName);
66+
isTrueArgument("collectionName is not empty", !collectionName.isEmpty());
67+
}
4668

69+
/**
70+
* Construct an instance for the given full name. The database name is the string preceding the first {@code "."} character.
71+
*
72+
* @param fullName the non-null full namespace
73+
* @see #checkDatabaseNameValidity(String)
74+
* @see #checkCollectionNameValidity(String)
75+
*/
76+
public MongoNamespace(final String fullName) {
77+
notNull("fullName", fullName);
78+
this.fullName = fullName;
4779
this.databaseName = getDatatabaseNameFromFullName(fullName);
4880
this.collectionName = getCollectionNameFullName(fullName);
49-
this.fullName = fullName;
81+
checkDatabaseNameValidity(databaseName);
82+
checkCollectionNameValidity(collectionName);
5083
}
5184

5285
/**
53-
* Construct an instance.
86+
* Construct an instance from the given database name and collection name.
5487
*
55-
* @param databaseName the non-null database name
56-
* @param collectionName the non-null collection name
88+
* @param databaseName the valid database name
89+
* @param collectionName the valid collection name
90+
* @see #checkDatabaseNameValidity(String)
91+
* @see #checkCollectionNameValidity(String)
5792
*/
5893
public MongoNamespace(final String databaseName, final String collectionName) {
59-
this.databaseName = notNull("databaseName", databaseName);
60-
this.collectionName = notNull("collectionName", collectionName);
61-
this.fullName = databaseName + "." + collectionName;
94+
checkDatabaseNameValidity(databaseName);
95+
checkCollectionNameValidity(collectionName);
96+
this.databaseName = databaseName;
97+
this.collectionName = collectionName;
98+
this.fullName = databaseName + '.' + collectionName;
6299
}
63100

64101
/**
@@ -126,29 +163,25 @@ public int hashCode() {
126163
return result;
127164
}
128165

129-
private static boolean isFullNameValid(final String fullName) {
130-
int firstDotIndex = fullName.indexOf(".");
131-
132-
if (firstDotIndex == -1) {
133-
return false;
134-
}
135-
if (firstDotIndex == 0) {
136-
return false;
137-
}
138-
if (fullName.charAt(fullName.length() - 1) == '.') {
139-
return false;
166+
private static String getCollectionNameFullName(final String namespace) {
167+
if (namespace == null) {
168+
return null;
140169
}
141-
if (fullName.charAt(firstDotIndex + 1) == '.') {
142-
return false;
170+
int firstDot = namespace.indexOf('.');
171+
if (firstDot == -1) {
172+
return namespace;
143173
}
144-
return true;
145-
}
146-
147-
private static String getCollectionNameFullName(final String namespace) {
148-
return namespace.substring(namespace.indexOf('.') + 1);
174+
return namespace.substring(firstDot + 1);
149175
}
150176

151177
private static String getDatatabaseNameFromFullName(final String namespace) {
152-
return namespace.substring(0, namespace.indexOf('.'));
178+
if (namespace == null) {
179+
return null;
180+
}
181+
int firstDot = namespace.indexOf('.');
182+
if (firstDot == -1) {
183+
return "";
184+
}
185+
return namespace.substring(0, firstDot);
153186
}
154187
}

driver-core/src/test/unit/com/mongodb/MongoNamespaceSpecification.groovy

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,28 +19,38 @@ package com.mongodb
1919
import spock.lang.Specification
2020

2121
class MongoNamespaceSpecification extends Specification {
22-
def 'null database name should throw IllegalArgumentException'() {
22+
def 'invalid database name should throw IllegalArgumentException'() {
2323
when:
24-
new MongoNamespace(null, 'test');
24+
new MongoNamespace(databaseName, 'test');
2525

2626
then:
2727
thrown(IllegalArgumentException)
28-
}
2928

30-
def 'null collection name should throw IllegalArgumentException'() {
3129
when:
32-
new MongoNamespace('test', null);
30+
MongoNamespace.checkDatabaseNameValidity(databaseName)
3331

3432
then:
3533
thrown(IllegalArgumentException)
34+
35+
where:
36+
databaseName << [null, '', 'a ', 'a.b' ]
3637
}
3738

38-
def 'null full name should throw IllegalArgumentException'() {
39+
def 'invalid collection name should throw IllegalArgumentException'() {
40+
when:
41+
new MongoNamespace('test', collectionName);
42+
43+
then:
44+
thrown(IllegalArgumentException)
45+
3946
when:
40-
new MongoNamespace(null)
47+
MongoNamespace.checkCollectionNameValidity(collectionName)
4148

4249
then:
4350
thrown(IllegalArgumentException)
51+
52+
where:
53+
collectionName << [null, '']
4454
}
4555

4656
def 'invalid full name should throw IllegalArgumentException'() {
@@ -51,7 +61,7 @@ class MongoNamespaceSpecification extends Specification {
5161
thrown(IllegalArgumentException)
5262

5363
where:
54-
fullName << ['db', '.db', 'db.', 'db..coll', 'db.coll.']
64+
fullName << [null, '', 'db', '.db', 'db.', 'a .b']
5565
}
5666

5767
def 'test getters'() {

driver/src/main/com/mongodb/DB.java

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949

5050
import static com.mongodb.DBCollection.createWriteConcernException;
5151
import static com.mongodb.MongoCredential.createMongoCRCredential;
52+
import static com.mongodb.MongoNamespace.checkDatabaseNameValidity;
5253
import static com.mongodb.ReadPreference.primary;
5354
import static com.mongodb.assertions.Assertions.notNull;
5455
import static java.util.Arrays.asList;
@@ -81,9 +82,7 @@ public class DB {
8182
private volatile ReadConcern readConcern;
8283

8384
DB(final Mongo mongo, final String name, final OperationExecutor executor) {
84-
if (!isValidName(name)) {
85-
throw new IllegalArgumentException("Invalid database name format. Database name is either empty or it contains spaces.");
86-
}
85+
checkDatabaseNameValidity(name);
8786
this.mongo = mongo;
8887
this.name = name;
8988
this.executor = executor;
@@ -184,6 +183,8 @@ public ReadConcern getReadConcern() {
184183
*
185184
* @param name the name of the collection
186185
* @return the collection
186+
* @throws IllegalArgumentException if the name is invalid
187+
* @see MongoNamespace#checkCollectionNameValidity(String)
187188
*/
188189
protected DBCollection doGetCollection(final String name) {
189190
return getCollection(name);
@@ -194,6 +195,8 @@ protected DBCollection doGetCollection(final String name) {
194195
*
195196
* @param name the name of the collection to return
196197
* @return the collection
198+
* @throws IllegalArgumentException if the name is invalid
199+
* @see MongoNamespace#checkCollectionNameValidity(String)
197200
*/
198201
public DBCollection getCollection(final String name) {
199202
DBCollection collection = collectionCache.get(name);
@@ -740,10 +743,6 @@ BufferProvider getBufferPool() {
740743
return getMongo().getBufferProvider();
741744
}
742745

743-
private boolean isValidName(final String databaseName) {
744-
return databaseName.length() != 0 && !databaseName.contains(" ");
745-
}
746-
747746
private BsonDocument wrap(final DBObject document) {
748747
return new BsonDocumentWrapper<DBObject>(document, commandCodec);
749748
}

driver/src/main/com/mongodb/DBCollection.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
import static com.mongodb.AggregationOptions.OutputMode.INLINE;
7878
import static com.mongodb.BulkWriteHelper.translateBulkWriteResult;
7979
import static com.mongodb.BulkWriteHelper.translateWriteRequestsToNew;
80+
import static com.mongodb.MongoNamespace.checkCollectionNameValidity;
8081
import static com.mongodb.ReadPreference.primary;
8182
import static com.mongodb.ReadPreference.primaryPreferred;
8283
import static com.mongodb.assertions.Assertions.notNull;
@@ -142,6 +143,7 @@ public class DBCollection {
142143
* @param database the database to which this collections belongs to
143144
*/
144145
DBCollection(final String name, final DB database, final OperationExecutor executor) {
146+
checkCollectionNameValidity(name);
145147
this.name = name;
146148
this.database = database;
147149
this.executor = executor;

driver/src/main/com/mongodb/Mongo.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,8 @@ public String apply(final DBObject result) {
483483
*
484484
* @param dbName the name of the database to retrieve
485485
* @return a DB representing the specified database
486+
* @throws IllegalArgumentException if the name is invalid
487+
* @see MongoNamespace#checkDatabaseNameValidity(String)
486488
* @deprecated use {@link com.mongodb.MongoClient#getDatabase(String)}
487489
*/
488490
@Deprecated

driver/src/main/com/mongodb/MongoClient.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,8 @@ public <T> ListDatabasesIterable<T> listDatabases(final Class<T> clazz) {
396396
/**
397397
* @param databaseName the name of the database to retrieve
398398
* @return a {@code MongoDatabase} representing the specified database
399+
* @throws IllegalArgumentException if databaseName is invalid
400+
* @see MongoNamespace#checkDatabaseNameValidity(String)
399401
*/
400402
public MongoDatabase getDatabase(final String databaseName) {
401403
MongoClientOptions clientOptions = getMongoClientOptions();

0 commit comments

Comments
 (0)