summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSpencer T Brody <spencer@10gen.com>2013-08-15 18:24:39 -0400
committerSpencer T Brody <spencer@10gen.com>2013-08-20 12:13:58 -0400
commita61d12943d0873c7b2c3f5f452501c1b759b3fa2 (patch)
tree65e3e1547f73a25281d384f65e0cbbc12b08ad6c
parent3615218927e28d12410841c3ca77f9d93e8246dc (diff)
downloadmongo-a61d12943d0873c7b2c3f5f452501c1b759b3fa2.tar.gz
SERVER-9517 Add parser for V2 privilege documents. Currently can only check if a document is valid.
-rw-r--r--src/mongo/db/auth/privilege_document_parser.cpp165
-rw-r--r--src/mongo/db/auth/privilege_document_parser.h24
-rw-r--r--src/mongo/db/auth/privilege_document_parser_test.cpp453
3 files changed, 449 insertions, 193 deletions
diff --git a/src/mongo/db/auth/privilege_document_parser.cpp b/src/mongo/db/auth/privilege_document_parser.cpp
index 6e086249bda..66eab7397e9 100644
--- a/src/mongo/db/auth/privilege_document_parser.cpp
+++ b/src/mongo/db/auth/privilege_document_parser.cpp
@@ -23,6 +23,7 @@
#include "mongo/db/auth/authorization_manager.h"
#include "mongo/db/auth/user.h"
#include "mongo/db/jsobj.h"
+#include "mongo/db/namespace_string.h"
#include "mongo/util/mongoutils/str.h"
namespace mongo {
@@ -34,6 +35,11 @@ namespace {
const std::string ROLES_FIELD_NAME = "roles";
const std::string OTHER_DB_ROLES_FIELD_NAME = "otherDBRoles";
const std::string READONLY_FIELD_NAME = "readOnly";
+ const std::string CREDENTIALS_FIELD_NAME = "credentials";
+ const std::string DELEGATABLE_ROLES_FIELD_NAME = "delegatableRoles";
+ const std::string ROLE_NAME_FIELD_NAME = "name";
+ const std::string ROLE_SOURCE_FIELD_NAME = "source";
+ const std::string MONGODB_CR_CREDENTIAL_FIELD_NAME = "MONGODB-CR";
const std::string SYSTEM_ROLE_READ = "read";
const std::string SYSTEM_ROLE_READ_WRITE = "readWrite";
@@ -251,8 +257,32 @@ namespace {
return allActions;
}
+ Status PrivilegeDocumentParser::initializeUserFromPrivilegeDocument(
+ User* user, const BSONObj& privDoc) const {
+ std::string userName = privDoc[AuthorizationManager::USER_NAME_FIELD_NAME].str();
+ if (userName != user->getName().getUser()) {
+ return Status(ErrorCodes::BadValue,
+ mongoutils::str::stream() << "User name from privilege document \""
+ << userName
+ << "\" doesn't match name of provided User \""
+ << user->getName().getUser()
+ << "\"",
+ 0);
+ }
+
+ Status status = initializeUserCredentialsFromPrivilegeDocument(user, privDoc);
+ if (!status.isOK()) {
+ return status;
+ }
+ status = initializeUserRolesFromPrivilegeDocument(user, privDoc, user->getName().getDB());
+ if (!status.isOK()) {
+ return status;
+ }
+ initializeUserPrivilegesFromRoles(user);
+ return Status::OK();
+ }
- Status _checkRolesArray(const BSONElement& rolesElement) {
+ Status _checkV1RolesArray(const BSONElement& rolesElement) {
if (rolesElement.type() != Array) {
return _badValue("Role fields must be an array when present in system.users entries",
0);
@@ -327,7 +357,7 @@ namespace {
// Validate the "roles" element.
if (!rolesElement.eoo()) {
- Status status = _checkRolesArray(rolesElement);
+ Status status = _checkV1RolesArray(rolesElement);
if (!status.isOK())
return status;
}
@@ -348,7 +378,7 @@ namespace {
for (BSONObjIterator iter(otherDBRolesElement.embeddedObject());
iter.more(); iter.next()) {
- Status status = _checkRolesArray(*iter);
+ Status status = _checkV1RolesArray(*iter);
if (!status.isOK())
return status;
}
@@ -568,29 +598,122 @@ namespace {
user->addPrivileges(privileges);
}
- Status V1PrivilegeDocumentParser::initializeUserFromPrivilegeDocument(
- User* user, const BSONObj& privDoc) const {
- std::string userName = privDoc[AuthorizationManager::USER_NAME_FIELD_NAME].str();
- if (userName != user->getName().getUser()) {
- return Status(ErrorCodes::BadValue,
- mongoutils::str::stream() << "User name from privilege document \""
- << userName
- << "\" doesn't match name of provided User \""
- << user->getName().getUser()
- << "\"",
- 0);
+ Status _checkV2RolesArray(const BSONElement& rolesElement) {
+ StringData fieldName = rolesElement.fieldNameStringData();
+
+ if (rolesElement.eoo()) {
+ return _badValue(mongoutils::str::stream() << "User document needs '" << fieldName <<
+ "' field to be provided",
+ 0);
+ }
+ if (rolesElement.type() != Array) {
+ return _badValue(mongoutils::str::stream() << fieldName << " field must be an array",
+ 0);
+ }
+ for (BSONObjIterator iter(rolesElement.embeddedObject()); iter.more(); iter.next()) {
+ if ((*iter).type() != Object) {
+ return _badValue(mongoutils::str::stream() << "Elements in '" << fieldName <<
+ "' array must objects.",
+ 0);
+ }
+ BSONObj roleObj = (*iter).Obj();
+ BSONElement nameElement = roleObj[ROLE_NAME_FIELD_NAME];
+ BSONElement sourceElement = roleObj[ROLE_SOURCE_FIELD_NAME];
+ if (nameElement.type() != String ||
+ makeStringDataFromBSONElement(nameElement).empty()) {
+ return _badValue(mongoutils::str::stream() << "Entries in '" << fieldName <<
+ "' array need 'name' field to be a non-empty string",
+ 0);
+ }
+ if (sourceElement.type() != String ||
+ makeStringDataFromBSONElement(sourceElement).empty()) {
+ return _badValue(mongoutils::str::stream() << "Entries in '" << fieldName <<
+ "' array need 'source' field to be a non-empty string",
+ 0);
+ }
}
+ return Status::OK();
+ }
- Status status = initializeUserCredentialsFromPrivilegeDocument(user, privDoc);
- if (!status.isOK()) {
- return status;
+ Status V2PrivilegeDocumentParser::checkValidPrivilegeDocument(const StringData& dbname,
+ const BSONObj& doc) const {
+ BSONElement userElement = doc[AuthorizationManager::USER_NAME_FIELD_NAME];
+ BSONElement userSourceElement = doc[AuthorizationManager::USER_SOURCE_FIELD_NAME];
+ BSONElement credentialsElement = doc[CREDENTIALS_FIELD_NAME];
+ BSONElement rolesElement = doc[ROLES_FIELD_NAME];
+ BSONElement delegatableRolesElement = doc[DELEGATABLE_ROLES_FIELD_NAME];
+
+ // Validate the "user" element.
+ if (userElement.type() != String)
+ return _badValue("User document needs 'user' field to be a string", 0);
+ if (makeStringDataFromBSONElement(userElement).empty())
+ return _badValue("User document needs 'user' field to be non-empty", 0);
+
+ // Validate the "userSource" element
+ if (userSourceElement.type() != String ||
+ makeStringDataFromBSONElement(userSourceElement).empty()) {
+ return _badValue("User document needs 'userSource' field to be a non-empty string", 0);
}
- status = initializeUserRolesFromPrivilegeDocument(user, privDoc, user->getName().getDB());
- if (!status.isOK()) {
- return status;
+ StringData userSourceStr = makeStringDataFromBSONElement(userSourceElement);
+ if (!NamespaceString::validDBName(userSourceStr) && userSourceStr != "$external") {
+ return _badValue(mongoutils::str::stream() << "'" << userSourceStr <<
+ "' is not a valid value for the userSource field.",
+ 0);
}
- initializeUserPrivilegesFromRoles(user);
+ if (userSourceStr != dbname) {
+ return _badValue(mongoutils::str::stream() << "userSource '" << userSourceStr <<
+ "' does not match database '" << dbname << "'", 0);
+ }
+
+ // Validate the "credentials" element
+ if (credentialsElement.eoo() && userSourceStr != "$external") {
+ return _badValue("User document needs 'credentials' field unless userSource is "
+ "'$external'",
+ 0);
+ }
+ if (!credentialsElement.eoo()) {
+ if (credentialsElement.type() != Object) {
+ return _badValue("User document needs 'credentials' field to be an object", 0);
+ }
+
+ BSONObj credentialsObj = credentialsElement.Obj();
+ if (credentialsObj.isEmpty()) {
+ return _badValue("User document needs 'credentials' field to be a non-empty object",
+ 0);
+ }
+ BSONElement MongoCRElement = credentialsObj[MONGODB_CR_CREDENTIAL_FIELD_NAME];
+ if (!MongoCRElement.eoo() && (MongoCRElement.type() != String ||
+ makeStringDataFromBSONElement(MongoCRElement).empty())) {
+ return _badValue("MONGODB-CR credential must to be a non-empty string, if present",
+ 0);
+ }
+ }
+
+ // Validate the "roles" element.
+ Status status = _checkV2RolesArray(rolesElement);
+ if (!status.isOK())
+ return status;
+
+ // Validate the "delegatableRoles" element.
+ status = _checkV2RolesArray(delegatableRolesElement);
+ if (!status.isOK())
+ return status;
+
return Status::OK();
}
+ Status V2PrivilegeDocumentParser::initializeUserCredentialsFromPrivilegeDocument(
+ User* user, const BSONObj& privDoc) const {
+ return Status(ErrorCodes::InternalError, "NOT YET IMPLEMENTED");
+ }
+
+ Status V2PrivilegeDocumentParser::initializeUserRolesFromPrivilegeDocument(
+ User* user, const BSONObj& privDoc, const StringData& dbname) const {
+ return Status(ErrorCodes::InternalError, "NOT YET IMPLEMENTED");
+ }
+
+ void V2PrivilegeDocumentParser::initializeUserPrivilegesFromRoles(User* user) const {
+ // NOT YET IMPELEMENTED
+ }
+
} // namespace mongo
diff --git a/src/mongo/db/auth/privilege_document_parser.h b/src/mongo/db/auth/privilege_document_parser.h
index 04e12375c64..90f2d34dc70 100644
--- a/src/mongo/db/auth/privilege_document_parser.h
+++ b/src/mongo/db/auth/privilege_document_parser.h
@@ -25,7 +25,7 @@
namespace mongo {
/**
- * Interface for class used to initialize User objects from their privilege documents
+ * Interface for class used to initialize User objects from their privilege documents.
*/
class PrivilegeDocumentParser {
MONGO_DISALLOW_COPYING(PrivilegeDocumentParser);
@@ -51,7 +51,7 @@ namespace mongo {
* with the information extracted from the privilege document.
*/
virtual Status initializeUserFromPrivilegeDocument(User* user,
- const BSONObj& privDoc) const = 0;
+ const BSONObj& privDoc) const;
/**
* Parses privDoc and initializes the user's "credentials" field with the credential
@@ -85,8 +85,24 @@ namespace mongo {
virtual Status checkValidPrivilegeDocument(const StringData& dbname,
const BSONObj& doc) const;
- virtual Status initializeUserFromPrivilegeDocument(User* user,
- const BSONObj& privDoc) const;
+ virtual Status initializeUserCredentialsFromPrivilegeDocument(User* user,
+ const BSONObj& privDoc) const;
+
+ virtual Status initializeUserRolesFromPrivilegeDocument(
+ User* user, const BSONObj& privDoc, const StringData& dbname) const;
+
+ virtual void initializeUserPrivilegesFromRoles(User* user) const;
+ };
+
+ class V2PrivilegeDocumentParser : public PrivilegeDocumentParser {
+ MONGO_DISALLOW_COPYING(V2PrivilegeDocumentParser);
+ public:
+
+ V2PrivilegeDocumentParser() {}
+ virtual ~V2PrivilegeDocumentParser() {}
+
+ virtual Status checkValidPrivilegeDocument(const StringData& dbname,
+ const BSONObj& doc) const;
virtual Status initializeUserCredentialsFromPrivilegeDocument(User* user,
const BSONObj& privDoc) const;
diff --git a/src/mongo/db/auth/privilege_document_parser_test.cpp b/src/mongo/db/auth/privilege_document_parser_test.cpp
index 539a1a9c0ee..d42a535fb86 100644
--- a/src/mongo/db/auth/privilege_document_parser_test.cpp
+++ b/src/mongo/db/auth/privilege_document_parser_test.cpp
@@ -23,9 +23,7 @@
#include "mongo/db/auth/authorization_manager.h"
#include "mongo/db/auth/privilege_document_parser.h"
#include "mongo/db/jsobj.h"
-#include "mongo/db/namespace_string.h"
#include "mongo/unittest/unittest.h"
-#include "mongo/util/map_util.h"
#define ASSERT_NULL(EXPR) ASSERT_FALSE(EXPR)
#define ASSERT_NON_NULL(EXPR) ASSERT_TRUE(EXPR)
@@ -33,8 +31,164 @@
namespace mongo {
namespace {
- TEST(PrivilegeDocumentParserTest, GetPrivilegesFromPrivilegeDocumentCompatible) {
- V1PrivilegeDocumentParser parser;
+ class PrivilegeDocumentParsing : public ::mongo::unittest::Test {
+ public:
+ PrivilegeDocumentParsing() {}
+
+ scoped_ptr<User> user;
+ scoped_ptr<User> adminUser;
+ V1PrivilegeDocumentParser v1parser;
+ V2PrivilegeDocumentParser v2parser;
+
+ void setUp() {
+ user.reset(new User(UserName("spencer", "test")));
+ adminUser.reset(new User(UserName("admin", "admin")));
+ }
+ };
+
+
+ TEST_F(PrivilegeDocumentParsing, V0DocumentValidation) {
+
+ // Good documents, with and without "readOnly" fields.
+ ASSERT_OK(v1parser.checkValidPrivilegeDocument(
+ "test", BSON("user" << "andy" << "pwd" << "a")));
+ ASSERT_OK(v1parser.checkValidPrivilegeDocument(
+ "test", BSON("user" << "andy" << "pwd" << "a" << "readOnly" << 1)));
+ ASSERT_OK(v1parser.checkValidPrivilegeDocument(
+ "test", BSON("user" << "andy" << "pwd" << "a" << "readOnly" << false)));
+ ASSERT_OK(v1parser.checkValidPrivilegeDocument(
+ "test", BSON("user" << "andy" << "pwd" << "a" << "readOnly" << "yes")));
+
+ // Must have a "pwd" field.
+ ASSERT_NOT_OK(v1parser.checkValidPrivilegeDocument(
+ "test", BSON("user" << "andy")));
+
+ // "pwd" field must be a string.
+ ASSERT_NOT_OK(v1parser.checkValidPrivilegeDocument(
+ "test", BSON("user" << "andy" << "pwd" << 100)));
+
+ // "pwd" field string must not be empty.
+ ASSERT_NOT_OK(v1parser.checkValidPrivilegeDocument(
+ "test", BSON("user" << "andy" << "pwd" << "")));
+
+ // Must have a "user" field.
+ ASSERT_NOT_OK(v1parser.checkValidPrivilegeDocument(
+ "test", BSON("pwd" << "a")));
+
+ // "user" field must be a string.
+ ASSERT_NOT_OK(v1parser.checkValidPrivilegeDocument(
+ "test", BSON("user" << 100 << "pwd" << "a")));
+
+ // "user" field string must not be empty.
+ ASSERT_NOT_OK(v1parser.checkValidPrivilegeDocument(
+ "test", BSON("user" << "" << "pwd" << "a")));
+ }
+
+
+ class CompatibilityModeDisabler {
+ public:
+ CompatibilityModeDisabler() {
+ AuthorizationManager::setSupportOldStylePrivilegeDocuments(false);
+ }
+ ~CompatibilityModeDisabler() {
+ AuthorizationManager::setSupportOldStylePrivilegeDocuments(true);
+ }
+ };
+
+ TEST_F(PrivilegeDocumentParsing, DisableCompatibilityMode) {
+ CompatibilityModeDisabler disabler;
+
+ ASSERT_NOT_OK(v1parser.checkValidPrivilegeDocument("test",
+ BSON("user" << "andy" << "pwd" << "a")));
+ ASSERT_NOT_OK(v1parser.checkValidPrivilegeDocument("test",
+ BSON("user" << "andy" << "pwd" << "a" << "readOnly" << 1)));
+ ASSERT_NOT_OK(v1parser.checkValidPrivilegeDocument("test",
+ BSON("user" << "andy" << "pwd" << "a" << "readOnly" << false)));
+ ASSERT_NOT_OK(v1parser.checkValidPrivilegeDocument("test",
+ BSON("user" << "andy" << "pwd" << "a" << "readOnly" << "yes")));
+
+ ASSERT_OK(v1parser.checkValidPrivilegeDocument("test",
+ BSON("user" << "andy" << "pwd" << "a" <<
+ "roles" << BSON_ARRAY("dbAdmin" << "read"))));
+ }
+
+ TEST_F(PrivilegeDocumentParsing, V1DocumentValidation) {
+ // Document describing new-style user on "test".
+ ASSERT_OK(v1parser.checkValidPrivilegeDocument("test",
+ BSON("user" << "andy" << "pwd" << "a" << "roles" << BSON_ARRAY("read"))));
+
+ // Document giving roles on "test" to a user from "test2".
+ ASSERT_OK(v1parser.checkValidPrivilegeDocument("test",
+ BSON("user" << "andy" << "userSource" << "test2" <<
+ "roles" << BSON_ARRAY("read"))));
+
+ // Cannot have "userSource" field value == dbname.
+ ASSERT_NOT_OK(v1parser.checkValidPrivilegeDocument("test",
+ BSON("user" << "andy" << "userSource" << "test" <<
+ "roles" << BSON_ARRAY("read"))));
+
+ // Cannot have both "userSource" and "pwd"
+ ASSERT_NOT_OK(v1parser.checkValidPrivilegeDocument("test",
+ BSON("user" << "andy" << "userSource" << "test2" << "pwd" << "a" <<
+ "roles" << BSON_ARRAY("read"))));
+
+ // Cannot have an otherDBRoles field except in the admin database.
+ ASSERT_NOT_OK(v1parser.checkValidPrivilegeDocument("test",
+ BSON("user" << "andy" << "userSource" << "test2" <<
+ "roles" << BSON_ARRAY("read") <<
+ "otherDBRoles" << BSON("test2" << BSON_ARRAY("readWrite")))));
+
+ ASSERT_OK(v1parser.checkValidPrivilegeDocument("admin",
+ BSON("user" << "andy" << "userSource" << "test2" <<
+ "roles" << BSON_ARRAY("read") <<
+ "otherDBRoles" << BSON("test2" << BSON_ARRAY("readWrite")))));
+
+ // Must have "roles" to have "otherDBRoles".
+ ASSERT_NOT_OK(v1parser.checkValidPrivilegeDocument("admin",
+ BSON("user" << "andy" << "pwd" << "a" <<
+ "otherDBRoles" << BSON("test2" << BSON_ARRAY("readWrite")))));
+
+ ASSERT_OK(v1parser.checkValidPrivilegeDocument("admin",
+ BSON("user" << "andy" << "pwd" << "a" <<
+ "roles" << BSONArrayBuilder().arr() <<
+ "otherDBRoles" << BSON("test2" << BSON_ARRAY("readWrite")))));
+
+ // "otherDBRoles" may be empty.
+ ASSERT_OK(v1parser.checkValidPrivilegeDocument("admin",
+ BSON("user" << "andy" << "pwd" << "a" <<
+ "roles" << BSONArrayBuilder().arr() <<
+ "otherDBRoles" << BSONObjBuilder().obj())));
+
+ // Cannot omit "roles" if "userSource" is present.
+ ASSERT_NOT_OK(v1parser.checkValidPrivilegeDocument("test",
+ BSON("user" << "andy" << "userSource" << "test2")));
+
+ // Cannot have both "roles" and "readOnly".
+ ASSERT_NOT_OK(v1parser.checkValidPrivilegeDocument("test",
+ BSON("user" << "andy" << "pwd" << "a" << "readOnly" << 1 <<
+ "roles" << BSON_ARRAY("read"))));
+
+ // Roles must be strings, not empty.
+ ASSERT_NOT_OK(v1parser.checkValidPrivilegeDocument("test",
+ BSON("user" << "andy" << "pwd" << "a" <<
+ "roles" << BSON_ARRAY("read" << ""))));
+
+ ASSERT_NOT_OK(v1parser.checkValidPrivilegeDocument("test",
+ BSON("user" << "andy" << "pwd" << "a" <<
+ "roles" << BSON_ARRAY(1 << "read"))));
+
+ // Multiple roles OK.
+ ASSERT_OK(v1parser.checkValidPrivilegeDocument("test",
+ BSON("user" << "andy" << "pwd" << "a" <<
+ "roles" << BSON_ARRAY("dbAdmin" << "read"))));
+
+ // Empty roles list OK.
+ ASSERT_OK(v1parser.checkValidPrivilegeDocument("test",
+ BSON("user" << "andy" << "pwd" << "a" <<
+ "roles" << BSONArrayBuilder().arr())));
+ }
+
+ TEST_F(PrivilegeDocumentParsing, testParsingV0PrivilegeDocuments) {
User user(UserName("Spencer", "test"));
User adminUser(UserName("Spencer", "admin"));
BSONObj invalid;
@@ -42,13 +196,13 @@ namespace {
BSONObj readOnly = BSON("user" << "Spencer" << "pwd" << "passwordHash" <<
"readOnly" << true);
- ASSERT_NOT_OK(parser.initializeUserFromPrivilegeDocument(&user, invalid));
+ ASSERT_NOT_OK(v1parser.initializeUserFromPrivilegeDocument(&user, invalid));
- ASSERT_OK(parser.initializeUserFromPrivilegeDocument(&user, readOnly));
+ ASSERT_OK(v1parser.initializeUserFromPrivilegeDocument(&user, readOnly));
ASSERT(user.getActionsForResource("test").contains(ActionType::find));
ASSERT(!user.getActionsForResource("test").contains(ActionType::insert));
- ASSERT_OK(parser.initializeUserFromPrivilegeDocument(&user, readWrite));
+ ASSERT_OK(v1parser.initializeUserFromPrivilegeDocument(&user, readWrite));
ASSERT(user.getActionsForResource("test").contains(ActionType::find));
ASSERT(user.getActionsForResource("test").contains(ActionType::insert));
ASSERT(user.getActionsForResource("test").contains(ActionType::userAdmin));
@@ -58,46 +212,32 @@ namespace {
ASSERT(!user.getActionsForResource("admin").contains(ActionType::find));
ASSERT(!user.getActionsForResource("*").contains(ActionType::find));
- ASSERT_OK(parser.initializeUserFromPrivilegeDocument(&adminUser, readOnly));
+ ASSERT_OK(v1parser.initializeUserFromPrivilegeDocument(&adminUser, readOnly));
ASSERT(adminUser.getActionsForResource("*").contains(ActionType::find));
ASSERT(!adminUser.getActionsForResource("admin").contains(ActionType::insert));
ASSERT(!adminUser.getActionsForResource("*").contains(ActionType::insert));
- ASSERT_OK(parser.initializeUserFromPrivilegeDocument(&adminUser, readWrite));
+ ASSERT_OK(v1parser.initializeUserFromPrivilegeDocument(&adminUser, readWrite));
ASSERT(adminUser.getActionsForResource("*").contains(ActionType::find));
ASSERT(adminUser.getActionsForResource("*").contains(ActionType::insert));
}
- class PrivilegeDocumentParsing : public ::mongo::unittest::Test {
- public:
- PrivilegeDocumentParsing() {}
-
- scoped_ptr<User> user;
- scoped_ptr<User> adminUser;
- V1PrivilegeDocumentParser parser;
-
- void setUp() {
- user.reset(new User(UserName("spencer", "test")));
- adminUser.reset(new User(UserName("admin", "admin")));
- }
- };
-
TEST_F(PrivilegeDocumentParsing, VerifyRolesFieldMustBeAnArray) {
- ASSERT_NOT_OK(parser.initializeUserFromPrivilegeDocument(
+ ASSERT_NOT_OK(v1parser.initializeUserFromPrivilegeDocument(
user.get(),
BSON("user" << "spencer" << "pwd" << "" << "roles" << "read")));
ASSERT(user->getActionsForResource("test").empty());
}
TEST_F(PrivilegeDocumentParsing, VerifyInvalidRoleGrantsNoPrivileges) {
- ASSERT_OK(parser.initializeUserFromPrivilegeDocument(
+ ASSERT_OK(v1parser.initializeUserFromPrivilegeDocument(
user.get(),
BSON("user" << "spencer" << "pwd" << "" << "roles" << BSON_ARRAY("frim"))));
ASSERT(user->getActionsForResource("test").empty());
}
TEST_F(PrivilegeDocumentParsing, VerifyInvalidRoleStillAllowsOtherRoles) {
- ASSERT_OK(parser.initializeUserFromPrivilegeDocument(
+ ASSERT_OK(v1parser.initializeUserFromPrivilegeDocument(
user.get(),
BSON("user" << "spencer" <<
"pwd" << "" <<
@@ -106,7 +246,7 @@ namespace {
}
TEST_F(PrivilegeDocumentParsing, VerifyCannotGrantClusterAdminRoleFromNonAdminDatabase) {
- ASSERT_OK(parser.initializeUserFromPrivilegeDocument(
+ ASSERT_OK(v1parser.initializeUserFromPrivilegeDocument(
user.get(),
BSON("user" << "spencer" <<
"pwd" << "" <<
@@ -117,7 +257,7 @@ namespace {
}
TEST_F(PrivilegeDocumentParsing, VerifyCannotGrantClusterReadFromNonAdminDatabase) {
- ASSERT_OK(parser.initializeUserFromPrivilegeDocument(
+ ASSERT_OK(v1parser.initializeUserFromPrivilegeDocument(
user.get(),
BSON("user" << "spencer" <<
"pwd" << "" <<
@@ -127,7 +267,7 @@ namespace {
}
TEST_F(PrivilegeDocumentParsing, VerifyCannotGrantClusterReadWriteFromNonAdminDatabase) {
- ASSERT_OK(parser.initializeUserFromPrivilegeDocument(
+ ASSERT_OK(v1parser.initializeUserFromPrivilegeDocument(
user.get(),
BSON("user" << "spencer" <<
"pwd" << "" <<
@@ -138,7 +278,7 @@ namespace {
}
TEST_F(PrivilegeDocumentParsing, VerifyCannotGrantClusterUserAdminFromNonAdminDatabase) {
- ASSERT_OK(parser.initializeUserFromPrivilegeDocument(
+ ASSERT_OK(v1parser.initializeUserFromPrivilegeDocument(
user.get(),
BSON("user" << "spencer" <<
"pwd" << "" <<
@@ -149,7 +289,7 @@ namespace {
}
TEST_F(PrivilegeDocumentParsing, VerifyCannotGrantClusterDBAdminFromNonAdminDatabase) {
- ASSERT_OK(parser.initializeUserFromPrivilegeDocument(
+ ASSERT_OK(v1parser.initializeUserFromPrivilegeDocument(
user.get(),
BSON("user" << "spencer" <<
"pwd" << "" <<
@@ -160,7 +300,7 @@ namespace {
}
TEST_F(PrivilegeDocumentParsing, VerifyOtherDBRolesMustBeAnObjectOfArraysOfStrings) {
- ASSERT_NOT_OK(parser.initializeUserFromPrivilegeDocument(
+ ASSERT_NOT_OK(v1parser.initializeUserFromPrivilegeDocument(
adminUser.get(),
BSON("user" << "admin" <<
"pwd" << "" <<
@@ -170,7 +310,7 @@ namespace {
ASSERT(!adminUser->getActionsForResource("test2").contains(ActionType::find));
ASSERT(!adminUser->getActionsForResource("admin").contains(ActionType::find));
- ASSERT_NOT_OK(parser.initializeUserFromPrivilegeDocument(
+ ASSERT_NOT_OK(v1parser.initializeUserFromPrivilegeDocument(
adminUser.get(),
BSON("user" << "admin" <<
"pwd" << "" <<
@@ -183,7 +323,7 @@ namespace {
TEST_F(PrivilegeDocumentParsing, VerifyCannotGrantPrivilegesOnOtherDatabasesNormally) {
// Cannot grant privileges on other databases, except from admin database.
- ASSERT_NOT_OK(parser.initializeUserFromPrivilegeDocument(
+ ASSERT_NOT_OK(v1parser.initializeUserFromPrivilegeDocument(
user.get(),
BSON("user" << "spencer" <<
"pwd" << "" <<
@@ -196,7 +336,7 @@ namespace {
TEST_F(PrivilegeDocumentParsing, SuccessfulSimpleReadGrant) {
// Grant read on test.
- ASSERT_OK(parser.initializeUserFromPrivilegeDocument(
+ ASSERT_OK(v1parser.initializeUserFromPrivilegeDocument(
user.get(),
BSON("user" << "spencer" <<
"pwd" << "" <<
@@ -208,7 +348,7 @@ namespace {
TEST_F(PrivilegeDocumentParsing, SuccessfulSimpleUserAdminTest) {
// Grant userAdmin on "test" database.
- ASSERT_OK(parser.initializeUserFromPrivilegeDocument(
+ ASSERT_OK(v1parser.initializeUserFromPrivilegeDocument(
user.get(),
BSON("user" << "spencer" <<
"pwd" << "" <<
@@ -220,7 +360,7 @@ namespace {
TEST_F(PrivilegeDocumentParsing, GrantUserAdminOnAdmin) {
// Grant userAdmin on admin.
- ASSERT_OK(parser.initializeUserFromPrivilegeDocument(
+ ASSERT_OK(v1parser.initializeUserFromPrivilegeDocument(
adminUser.get(),
BSON("user" << "admin" <<
"pwd" << "" <<
@@ -232,7 +372,7 @@ namespace {
TEST_F(PrivilegeDocumentParsing, GrantUserAdminOnTestViaAdmin) {
// Grant userAdmin on test via admin.
- ASSERT_OK(parser.initializeUserFromPrivilegeDocument(
+ ASSERT_OK(v1parser.initializeUserFromPrivilegeDocument(
adminUser.get(),
BSON("user" << "admin" <<
"pwd" << "" <<
@@ -245,7 +385,7 @@ namespace {
TEST_F(PrivilegeDocumentParsing, SuccessfulClusterAdminTest) {
// Grant userAdminAnyDatabase.
- ASSERT_OK(parser.initializeUserFromPrivilegeDocument(
+ ASSERT_OK(v1parser.initializeUserFromPrivilegeDocument(
adminUser.get(),
BSON("user" << "admin" <<
"pwd" << "" <<
@@ -256,7 +396,7 @@ namespace {
TEST_F(PrivilegeDocumentParsing, GrantClusterReadWrite) {
// Grant readWrite on everything via the admin database.
- ASSERT_OK(parser.initializeUserFromPrivilegeDocument(
+ ASSERT_OK(v1parser.initializeUserFromPrivilegeDocument(
adminUser.get(),
BSON("user" << "admin" <<
"pwd" << "" <<
@@ -267,7 +407,7 @@ namespace {
TEST_F(PrivilegeDocumentParsing, ProhibitGrantOnWildcard) {
// Cannot grant readWrite to everything using "otherDBRoles".
- ASSERT_NOT_OK(parser.initializeUserFromPrivilegeDocument(
+ ASSERT_NOT_OK(v1parser.initializeUserFromPrivilegeDocument(
adminUser.get(),
BSON("user" << "admin" <<
"pwd" << "" <<
@@ -283,7 +423,7 @@ namespace {
TEST_F(PrivilegeDocumentParsing, GrantClusterAdmin) {
// Grant cluster admin
- ASSERT_OK(parser.initializeUserFromPrivilegeDocument(
+ ASSERT_OK(v1parser.initializeUserFromPrivilegeDocument(
adminUser.get(),
BSON("user" << "admin" <<
"pwd" << "" <<
@@ -295,7 +435,7 @@ namespace {
TEST_F(PrivilegeDocumentParsing, GetPrivilegesFromPrivilegeDocumentInvalid) {
// Try to mix fields from V0 and V1 privilege documents and make sure it fails.
- ASSERT_NOT_OK(parser.initializeUserFromPrivilegeDocument(
+ ASSERT_NOT_OK(v1parser.initializeUserFromPrivilegeDocument(
user.get(),
BSON("user" << "spencer" <<
"pwd" << "passwordHash" <<
@@ -304,145 +444,122 @@ namespace {
ASSERT(!adminUser->getActionsForResource("test").contains(ActionType::find));
}
- TEST_F(PrivilegeDocumentParsing, DocumentValidationCompatibility) {
+ TEST_F(PrivilegeDocumentParsing, V2DocumentValidation) {
+ BSONArray emptyArray = BSONArrayBuilder().arr();
- // Good documents, with and without "readOnly" fields.
- ASSERT_OK(parser.checkValidPrivilegeDocument(
- "test", BSON("user" << "andy" << "pwd" << "a")));
- ASSERT_OK(parser.checkValidPrivilegeDocument(
- "test", BSON("user" << "andy" << "pwd" << "a" << "readOnly" << 1)));
- ASSERT_OK(parser.checkValidPrivilegeDocument(
- "test", BSON("user" << "andy" << "pwd" << "a" << "readOnly" << false)));
- ASSERT_OK(parser.checkValidPrivilegeDocument(
- "test", BSON("user" << "andy" << "pwd" << "a" << "readOnly" << "yes")));
-
- // Must have a "pwd" field.
- ASSERT_NOT_OK(parser.checkValidPrivilegeDocument(
- "test", BSON("user" << "andy")));
-
- // "pwd" field must be a string.
- ASSERT_NOT_OK(parser.checkValidPrivilegeDocument(
- "test", BSON("user" << "andy" << "pwd" << 100)));
-
- // "pwd" field string must not be empty.
- ASSERT_NOT_OK(parser.checkValidPrivilegeDocument(
- "test", BSON("user" << "andy" << "pwd" << "")));
-
- // Must have a "user" field.
- ASSERT_NOT_OK(parser.checkValidPrivilegeDocument(
- "test", BSON("pwd" << "a")));
-
- // "user" field must be a string.
- ASSERT_NOT_OK(parser.checkValidPrivilegeDocument(
- "test", BSON("user" << 100 << "pwd" << "a")));
-
- // "user" field string must not be empty.
- ASSERT_NOT_OK(parser.checkValidPrivilegeDocument(
- "test", BSON("user" << "" << "pwd" << "a")));
- }
-
-
- class CompatibilityModeDisabler {
- public:
- CompatibilityModeDisabler() {
- AuthorizationManager::setSupportOldStylePrivilegeDocuments(false);
- }
- ~CompatibilityModeDisabler() {
- AuthorizationManager::setSupportOldStylePrivilegeDocuments(true);
- }
- };
-
- TEST_F(PrivilegeDocumentParsing, DisableCompatibilityMode) {
- CompatibilityModeDisabler disabler;
-
- ASSERT_NOT_OK(parser.checkValidPrivilegeDocument("test",
- BSON("user" << "andy" << "pwd" << "a")));
- ASSERT_NOT_OK(parser.checkValidPrivilegeDocument("test",
- BSON("user" << "andy" << "pwd" << "a" << "readOnly" << 1)));
- ASSERT_NOT_OK(parser.checkValidPrivilegeDocument("test",
- BSON("user" << "andy" << "pwd" << "a" << "readOnly" << false)));
- ASSERT_NOT_OK(parser.checkValidPrivilegeDocument("test",
- BSON("user" << "andy" << "pwd" << "a" << "readOnly" << "yes")));
-
- ASSERT_OK(parser.checkValidPrivilegeDocument("test",
- BSON("user" << "andy" << "pwd" << "a" <<
- "roles" << BSON_ARRAY("dbAdmin" << "read"))));
- }
-
- TEST_F(PrivilegeDocumentParsing, DocumentValidationExtended) {
- // Document describing new-style user on "test".
- ASSERT_OK(parser.checkValidPrivilegeDocument("test",
- BSON("user" << "andy" << "pwd" << "a" << "roles" << BSON_ARRAY("read"))));
-
- // Document giving roles on "test" to a user from "test2".
- ASSERT_OK(parser.checkValidPrivilegeDocument("test",
- BSON("user" << "andy" << "userSource" << "test2" <<
+ // V1 documents don't work
+ ASSERT_NOT_OK(v2parser.checkValidPrivilegeDocument("test",
+ BSON("user" << "spencer" << "pwd" << "a" <<
"roles" << BSON_ARRAY("read"))));
- // Cannot have "userSource" field value == dbname.
- ASSERT_NOT_OK(parser.checkValidPrivilegeDocument("test",
- BSON("user" << "andy" << "userSource" << "test" <<
- "roles" << BSON_ARRAY("read"))));
+ // Need user field
+ ASSERT_NOT_OK(v2parser.checkValidPrivilegeDocument("test",
+ BSON("userSource" << "test" <<
+ "credentials" << BSON("MONGODB-CR" << "a") <<
+ "roles" << emptyArray <<
+ "delegatableRoles" << emptyArray)));
- // Cannot have both "userSource" and "pwd"
- ASSERT_NOT_OK(parser.checkValidPrivilegeDocument("test",
- BSON("user" << "andy" << "userSource" << "test2" << "pwd" << "a" <<
- "roles" << BSON_ARRAY("read"))));
-
- // Cannot have an otherDBRoles field except in the admin database.
- ASSERT_NOT_OK(parser.checkValidPrivilegeDocument("test",
- BSON("user" << "andy" << "userSource" << "test2" <<
- "roles" << BSON_ARRAY("read") <<
- "otherDBRoles" << BSON("test2" << BSON_ARRAY("readWrite")))));
+ // Need userSource field
+ ASSERT_NOT_OK(v2parser.checkValidPrivilegeDocument("test",
+ BSON("user" << "spencer" <<
+ "credentials" << BSON("MONGODB-CR" << "a") <<
+ "roles" << emptyArray <<
+ "delegatableRoles" << emptyArray)));
- ASSERT_OK(parser.checkValidPrivilegeDocument("admin",
- BSON("user" << "andy" << "userSource" << "test2" <<
- "roles" << BSON_ARRAY("read") <<
- "otherDBRoles" << BSON("test2" << BSON_ARRAY("readWrite")))));
+ // Need credentials field
+ ASSERT_NOT_OK(v2parser.checkValidPrivilegeDocument("test",
+ BSON("user" << "spencer" <<
+ "userSource" << "test" <<
+ "roles" << emptyArray <<
+ "delegatableRoles" << emptyArray)));
- // Must have "roles" to have "otherDBRoles".
- ASSERT_NOT_OK(parser.checkValidPrivilegeDocument("admin",
- BSON("user" << "andy" << "pwd" << "a" <<
- "otherDBRoles" << BSON("test2" << BSON_ARRAY("readWrite")))));
+ // Need roles field
+ ASSERT_NOT_OK(v2parser.checkValidPrivilegeDocument("test",
+ BSON("user" << "spencer" <<
+ "userSource" << "test" <<
+ "credentials" << BSON("MONGODB-CR" << "a") <<
+ "delegatableRoles" << emptyArray)));
- ASSERT_OK(parser.checkValidPrivilegeDocument("admin",
- BSON("user" << "andy" << "pwd" << "a" <<
- "roles" << BSONArrayBuilder().arr() <<
- "otherDBRoles" << BSON("test2" << BSON_ARRAY("readWrite")))));
+ // Need delegatableRoles field
+ ASSERT_NOT_OK(v2parser.checkValidPrivilegeDocument("test",
+ BSON("user" << "spencer" <<
+ "userSource" << "test" <<
+ "credentials" << BSON("MONGODB-CR" << "a") <<
+ "roles" << emptyArray)));
- // "otherDBRoles" may be empty.
- ASSERT_OK(parser.checkValidPrivilegeDocument("admin",
- BSON("user" << "andy" << "pwd" << "a" <<
- "roles" << BSONArrayBuilder().arr() <<
- "otherDBRoles" << BSONObjBuilder().obj())));
+ // db the user command is run on must match the userSource
+ ASSERT_NOT_OK(v2parser.checkValidPrivilegeDocument("test",
+ BSON("user" << "spencer" <<
+ "userSource" << "test2" <<
+ "credentials" << BSON("MONGODB-CR" << "a") <<
+ "roles" << emptyArray <<
+ "delegatableRoles" << emptyArray)));
- // Cannot omit "roles" if "userSource" is present.
- ASSERT_NOT_OK(parser.checkValidPrivilegeDocument("test",
- BSON("user" << "andy" << "userSource" << "test2")));
+ // Don't need credentials field if userSource is $external
+ ASSERT_OK(v2parser.checkValidPrivilegeDocument("$external",
+ BSON("user" << "spencer" <<
+ "userSource" << "$external" <<
+ "roles" << emptyArray <<
+ "delegatableRoles" << emptyArray)));
- // Cannot have both "roles" and "readOnly".
- ASSERT_NOT_OK(parser.checkValidPrivilegeDocument("test",
- BSON("user" << "andy" << "pwd" << "a" << "readOnly" << 1 <<
- "roles" << BSON_ARRAY("read"))));
+ // Empty roles arrays are OK
+ ASSERT_OK(v2parser.checkValidPrivilegeDocument("test",
+ BSON("user" << "spencer" <<
+ "userSource" << "test" <<
+ "credentials" << BSON("MONGODB-CR" << "a") <<
+ "roles" << emptyArray <<
+ "delegatableRoles" << emptyArray)));
- // Roles must be strings, not empty.
- ASSERT_NOT_OK(parser.checkValidPrivilegeDocument("test",
- BSON("user" << "andy" << "pwd" << "a" <<
- "roles" << BSON_ARRAY("read" << ""))));
+ // Roles must be objects
+ ASSERT_NOT_OK(v2parser.checkValidPrivilegeDocument("test",
+ BSON("user" << "spencer" <<
+ "userSource" << "test" <<
+ "credentials" << BSON("MONGODB-CR" << "a") <<
+ "roles" << BSON_ARRAY("read") <<
+ "delegatableRoles" << emptyArray)));
- ASSERT_NOT_OK(parser.checkValidPrivilegeDocument("test",
- BSON("user" << "andy" << "pwd" << "a" <<
- "roles" << BSON_ARRAY(1 << "read"))));
+ // Role needs source
+ ASSERT_NOT_OK(v2parser.checkValidPrivilegeDocument("test",
+ BSON("user" << "spencer" <<
+ "userSource" << "test" <<
+ "credentials" << BSON("MONGODB-CR" << "a") <<
+ "roles" << BSON_ARRAY(BSON("name" << "roleA")) <<
+ "delegatableRoles" << emptyArray)));
- // Multiple roles OK.
- ASSERT_OK(parser.checkValidPrivilegeDocument("test",
- BSON("user" << "andy" << "pwd" << "a" <<
- "roles" << BSON_ARRAY("dbAdmin" << "read"))));
+ // Role needs name
+ ASSERT_NOT_OK(v2parser.checkValidPrivilegeDocument("test",
+ BSON("user" << "spencer" <<
+ "userSource" << "test" <<
+ "credentials" << BSON("MONGODB-CR" << "a") <<
+ "roles" << BSON_ARRAY(BSON("source" << "dbA")) <<
+ "delegatableRoles" << emptyArray)));
- // Empty roles list OK.
- ASSERT_OK(parser.checkValidPrivilegeDocument("test",
- BSON("user" << "andy" << "pwd" << "a" <<
- "roles" << BSONArrayBuilder().arr())));
+ // Basic valid privilege document
+ ASSERT_OK(v2parser.checkValidPrivilegeDocument("test",
+ BSON("user" << "spencer" <<
+ "userSource" << "test" <<
+ "credentials" << BSON("MONGODB-CR" << "a") <<
+ "roles" << BSON_ARRAY(BSON("name" << "roleA" << "source" << "dbA")) <<
+ "delegatableRoles" << BSON_ARRAY(BSON("name" << "roleA" <<
+ "source" << "dbA")))));
+
+ // Multiple roles OK
+ ASSERT_OK(v2parser.checkValidPrivilegeDocument("test",
+ BSON("user" << "spencer" <<
+ "userSource" << "test" <<
+ "credentials" << BSON("MONGODB-CR" << "a") <<
+ "roles" << BSON_ARRAY(BSON("name" << "roleA" << "source" << "dbA") <<
+ BSON("name" << "roleB" << "source" << "dbB")) <<
+ "delegatableRoles" << emptyArray)));
+
+ // Optional extraData field OK
+ ASSERT_OK(v2parser.checkValidPrivilegeDocument("test",
+ BSON("user" << "spencer" <<
+ "userSource" << "test" <<
+ "credentials" << BSON("MONGODB-CR" << "a") <<
+ "extraData" << BSON("foo" << "bar") <<
+ "roles" << BSON_ARRAY(BSON("name" << "roleA" << "source" << "dbA")) <<
+ "delegatableRoles" << emptyArray)));
}
} // namespace