Skip to content

Commit 56d8b77

Browse files
committed
PHPC-424: Validate that RP tag set is an array of documents
This adds common validation for read preference tag sets when specified through either the Manager constructor's URI options array or ReadPreference constructor. An additional test case for a malformed tag set has been added to the Manager::__construct() error test for read preference options. Additionally, the ReadPreference::__construct() error test has been split up to test for mode and tagSet errors separately. Note: we cannot test for the exceptions for bson_init_static() and mongoc_read_prefs_is_valid(), since those points will never be hit in normal operation.
1 parent 0bfde7d commit 56d8b77

File tree

6 files changed

+105
-20
lines changed

6 files changed

+105
-20
lines changed

php_phongo.c

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -936,6 +936,29 @@ void php_phongo_read_concern_to_zval(zval *retval, const mongoc_read_concern_t *
936936
}
937937
} /* }}} */
938938

939+
/* Checks if tags is valid to set on a mongoc_read_prefs_t. It may be null or an
940+
* array of one or more documents. */
941+
bool php_phongo_read_preference_tags_are_valid(const bson_t *tags) /* {{{ */
942+
{
943+
bson_iter_t iter;
944+
945+
if (bson_empty0(tags)) {
946+
return true;
947+
}
948+
949+
if (!bson_iter_init(&iter, tags)) {
950+
return false;
951+
}
952+
953+
while (bson_iter_next(&iter)) {
954+
if (!BSON_ITER_HOLDS_DOCUMENT(&iter) && !BSON_ITER_HOLDS_ARRAY(&iter)) {
955+
return false;
956+
}
957+
}
958+
959+
return true;
960+
} /* }}} */
961+
939962
void php_phongo_read_preference_to_zval(zval *retval, const mongoc_read_prefs_t *read_prefs) /* {{{ */
940963
{
941964
const bson_t *tags = mongoc_read_prefs_get_tags(read_prefs);
@@ -1143,9 +1166,21 @@ static bool php_phongo_apply_rp_options_to_uri(mongoc_uri_t *uri, bson_t *option
11431166

11441167
bson_iter_array(&iter, &len, &data);
11451168

1146-
if (bson_init_static(&tags, data, len)) {
1147-
mongoc_read_prefs_set_tags(new_rp, &tags);
1169+
if (!bson_init_static(&tags, data, len)) {
1170+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Could not initialize BSON structure for read preference tags");
1171+
mongoc_read_prefs_destroy(new_rp);
1172+
1173+
return false;
1174+
}
1175+
1176+
if (!php_phongo_read_preference_tags_are_valid(&tags)) {
1177+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Read preference tags must be an array of zero or more documents");
1178+
mongoc_read_prefs_destroy(new_rp);
1179+
1180+
return false;
11481181
}
1182+
1183+
mongoc_read_prefs_set_tags(new_rp, &tags);
11491184
}
11501185

11511186
if (mongoc_read_prefs_get_mode(new_rp) == MONGOC_READ_PRIMARY &&

php_phongo.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ const mongoc_write_concern_t* phongo_write_concern_from_zval (zval *zwrite_conc
129129

130130
php_phongo_server_description_type_t php_phongo_server_description_type(mongoc_server_description_t *sd);
131131

132+
bool php_phongo_read_preference_tags_are_valid(const bson_t *tags);
133+
132134
void php_phongo_server_to_zval(zval *retval, mongoc_server_description_t *sd);
133135
void php_phongo_read_concern_to_zval(zval *retval, const mongoc_read_concern_t *read_concern);
134136
void php_phongo_read_preference_to_zval(zval *retval, const mongoc_read_prefs_t *read_prefs);

src/MongoDB/ReadPreference.c

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -74,22 +74,39 @@ PHP_METHOD(ReadPreference, __construct)
7474
case MONGOC_READ_SECONDARY_PREFERRED:
7575
case MONGOC_READ_NEAREST:
7676
intern->read_preference = mongoc_read_prefs_new(mode);
77+
break;
78+
default:
79+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Invalid mode: %" PHONGO_LONG_FORMAT, mode);
80+
return;
81+
}
7782

83+
switch(ZEND_NUM_ARGS()) {
84+
case 2:
7885
if (tagSets) {
7986
bson_t *tags = bson_new();
8087

8188
phongo_zval_to_bson(tagSets, PHONGO_BSON_NONE, (bson_t *)tags, NULL TSRMLS_CC);
82-
mongoc_read_prefs_set_tags(intern->read_preference, tags);
83-
bson_destroy(tags);
84-
if (!mongoc_read_prefs_is_valid(intern->read_preference)) {
85-
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Invalid tagSets");
89+
90+
if (!php_phongo_read_preference_tags_are_valid(tags)) {
91+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "tagSets must be an array of zero or more documents");
92+
bson_destroy(tags);
93+
return;
94+
}
95+
96+
if (!bson_empty(tags) && mode == MONGOC_READ_PRIMARY) {
97+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "tagSets may not be used with primary mode");
98+
bson_destroy(tags);
8699
return;
87100
}
101+
102+
mongoc_read_prefs_set_tags(intern->read_preference, tags);
103+
bson_destroy(tags);
88104
}
89-
break;
90-
default:
91-
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Invalid mode: %" PHONGO_LONG_FORMAT, mode);
92-
return;
105+
}
106+
107+
if (!mongoc_read_prefs_is_valid(intern->read_preference)) {
108+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Read preference is not valid");
109+
return;
93110
}
94111
}
95112
/* }}} */

tests/manager/manager-ctor_error-003.phpt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,15 @@ echo throws(function() {
1111
}, "MongoDB\Driver\Exception\InvalidArgumentException"), "\n";
1212

1313
echo throws(function() {
14-
$manager = new MongoDB\Driver\Manager(STANDALONE, array('readPreference' => 'nothing'));
14+
$manager = new MongoDB\Driver\Manager(STANDALONE, ['readPreference' => 'nothing']);
1515
}, "MongoDB\Driver\Exception\InvalidArgumentException"), "\n";
1616

1717
echo throws(function() {
18-
$manager = new MongoDB\Driver\Manager(STANDALONE . '/?readPreference=primary', array('readPreferenceTags' => array(array())));
18+
$manager = new MongoDB\Driver\Manager(STANDALONE . '/?readPreference=primary', ['readPreferenceTags' => [[]]]);
19+
}, "MongoDB\Driver\Exception\InvalidArgumentException"), "\n";
20+
21+
echo throws(function() {
22+
$manager = new MongoDB\Driver\Manager(STANDALONE . '/?readPreference=primary', ['readPreferenceTags' => ['invalid']]);
1923
}, "MongoDB\Driver\Exception\InvalidArgumentException"), "\n";
2024

2125
?>
@@ -28,4 +32,6 @@ OK: Got MongoDB\Driver\Exception\InvalidArgumentException
2832
Unsupported readPreference value: 'nothing'
2933
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
3034
Primary read preference mode conflicts with tags
35+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
36+
Read preference tags must be an array of zero or more documents
3137
===DONE===
Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,19 @@
11
--TEST--
2-
MongoDB\Driver\ReadPreference construction (invalid arguments)
2+
MongoDB\Driver\ReadPreference construction (invalid mode)
33
--SKIPIF--
44
<?php require __DIR__ . "/../utils/basic-skipif.inc"?>
55
--FILE--
66
<?php
77
require_once __DIR__ . "/../utils/basic.inc";
88

9-
echo throws(function() {
10-
new MongoDB\Driver\ReadPreference(MongoDB\Driver\ReadPreference::RP_PRIMARY, array(array("tag" => "one")));
11-
}, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n";
12-
139
echo throws(function() {
1410
new MongoDB\Driver\ReadPreference(42);
1511
}, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n";
1612

1713
?>
1814
===DONE===
1915
<?php exit(0); ?>
20-
--EXPECTF--
21-
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
22-
Invalid tagSets
16+
--EXPECT--
2317
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
2418
Invalid mode: 42
2519
===DONE===
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
--TEST--
2+
MongoDB\Driver\ReadPreference construction (invalid tagSets)
3+
--SKIPIF--
4+
<?php require __DIR__ . "/../utils/basic-skipif.inc"?>
5+
--FILE--
6+
<?php
7+
require_once __DIR__ . "/../utils/basic.inc";
8+
9+
echo throws(function() {
10+
new MongoDB\Driver\ReadPreference(MongoDB\Driver\ReadPreference::RP_PRIMARY, [['tag' => 'one']]);
11+
}, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n";
12+
13+
echo throws(function() {
14+
new MongoDB\Driver\ReadPreference(MongoDB\Driver\ReadPreference::RP_PRIMARY, ['invalid']);
15+
}, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n";
16+
17+
echo throws(function() {
18+
new MongoDB\Driver\ReadPreference(MongoDB\Driver\ReadPreference::RP_SECONDARY, ['invalid']);
19+
}, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n";
20+
21+
?>
22+
===DONE===
23+
<?php exit(0); ?>
24+
--EXPECT--
25+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
26+
tagSets may not be used with primary mode
27+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
28+
tagSets must be an array of zero or more documents
29+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
30+
tagSets must be an array of zero or more documents
31+
===DONE===

0 commit comments

Comments
 (0)