diff options
author | Spencer T Brody <spencer@10gen.com> | 2013-08-15 18:24:39 -0400 |
---|---|---|
committer | Spencer T Brody <spencer@10gen.com> | 2013-08-20 12:13:58 -0400 |
commit | a61d12943d0873c7b2c3f5f452501c1b759b3fa2 (patch) | |
tree | 65e3e1547f73a25281d384f65e0cbbc12b08ad6c | |
parent | 3615218927e28d12410841c3ca77f9d93e8246dc (diff) | |
download | mongo-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.cpp | 165 | ||||
-rw-r--r-- | src/mongo/db/auth/privilege_document_parser.h | 24 | ||||
-rw-r--r-- | src/mongo/db/auth/privilege_document_parser_test.cpp | 453 |
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 |