summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mongo/db/auth/SConscript3
-rw-r--r--src/mongo/db/auth/user.h2
-rw-r--r--src/mongo/db/auth/user_management_commands_parser.cpp329
-rw-r--r--src/mongo/db/auth/user_management_commands_parser.h83
-rw-r--r--src/mongo/db/auth/user_management_commands_parser_test.cpp785
-rw-r--r--src/mongo/db/commands/user_management_commands.cpp360
6 files changed, 457 insertions, 1105 deletions
diff --git a/src/mongo/db/auth/SConscript b/src/mongo/db/auth/SConscript
index 5d699f6a4a3..61ed510c6ea 100644
--- a/src/mongo/db/auth/SConscript
+++ b/src/mongo/db/auth/SConscript
@@ -68,6 +68,3 @@ env.CppUnitTest('authorization_manager_test', 'authorization_manager_test.cpp',
LIBDEPS=['authcore', 'authmocks'])
env.CppUnitTest('authorization_session_test', 'authorization_session_test.cpp',
LIBDEPS=['authcore', 'authmocks'])
-env.CppUnitTest('user_management_commands_parser_test', 'user_management_commands_parser_test.cpp',
- LIBDEPS=['usercommandsparser', 'authmocks'],
- NO_CRUTCH=True)
diff --git a/src/mongo/db/auth/user.h b/src/mongo/db/auth/user.h
index 2d6e895b500..24f588cd335 100644
--- a/src/mongo/db/auth/user.h
+++ b/src/mongo/db/auth/user.h
@@ -55,6 +55,8 @@ namespace mongo {
bool hasRole;
bool canDelegate;
RoleData() : hasRole(false), canDelegate(false) {}
+ RoleData(const RoleName& _name, bool _hasRole, bool _canDelegate) :
+ name(_name), hasRole(_hasRole), canDelegate(_canDelegate) {}
};
typedef unordered_map<RoleName, RoleData> RoleDataMap;
diff --git a/src/mongo/db/auth/user_management_commands_parser.cpp b/src/mongo/db/auth/user_management_commands_parser.cpp
index 19526fe7210..2678cbd9b0b 100644
--- a/src/mongo/db/auth/user_management_commands_parser.cpp
+++ b/src/mongo/db/auth/user_management_commands_parser.cpp
@@ -84,22 +84,15 @@ namespace auth {
/**
* Takes a BSONArray of name,source pair documents, parses that array and returns (via the
* output param parsedRoleNames) a list of the role names in the input array.
- * Also validates the input array and returns a non-OK status if there is anything wrong.
*/
- Status _extractRoleNamesFromBSONArray(const BSONArray rolesArray,
- const std::string& dbname,
- AuthorizationManager* authzManager,
- std::vector<RoleName>* parsedRoleNames) {
+ Status _extractRoleNamesFromBSONArray(const BSONArray& rolesArray,
+ const std::string& dbname,
+ const StringData& rolesFieldName,
+ std::vector<RoleName>* parsedRoleNames) {
for (BSONObjIterator it(rolesArray); it.more(); it.next()) {
BSONElement element = *it;
if (element.type() == String) {
- RoleName roleName(element.String(), dbname);
- if (!authzManager->roleExists(roleName)) {
- return Status(ErrorCodes::RoleNotFound,
- mongoutils::str::stream() << roleName.getFullName() <<
- " does not name an existing role");
- }
- parsedRoleNames->push_back(roleName);
+ parsedRoleNames->push_back(RoleName(element.String(), dbname));
} else if (element.type() == Object) {
BSONObj roleObj = element.Obj();
@@ -114,28 +107,73 @@ namespace auth {
return status;
}
- RoleName roleName(roleNameString, roleSource);
- if (!authzManager->roleExists(roleName)) {
- return Status(ErrorCodes::RoleNotFound,
- mongoutils::str::stream() << roleName.getFullName() <<
- " does not name an existing role");
- }
- parsedRoleNames->push_back(roleName);
+ parsedRoleNames->push_back(RoleName(roleNameString, roleSource));
} else {
- return Status(ErrorCodes::UnsupportedFormat,
- "Values in 'roles' array must be sub-documents or strings");
+ return Status(ErrorCodes::BadValue,
+ mongoutils::str::stream() << "Values in \"" << rolesFieldName <<
+ "\" array must be sub-documents or strings");
}
}
return Status::OK();
}
- Status parseUserRoleManipulationCommand(const BSONObj& cmdObj,
- const StringData& cmdName,
- const std::string& dbname,
- AuthorizationManager* authzManager,
- UserName* parsedUserName,
- vector<RoleName>* parsedRoleNames,
- BSONObj* parsedWriteConcern) {
+ Status _extractRoleDataFromBSONArray(const BSONElement& rolesElement,
+ const std::string& dbname,
+ std::vector<User::RoleData> *parsedRoleData) {
+ for (BSONObjIterator it(rolesElement.Obj()); it.more(); it.next()) {
+ BSONElement element = *it;
+ if (element.type() == String) {
+ RoleName roleName(element.String(), dbname);
+ parsedRoleData->push_back(User::RoleData(roleName, true, false));
+ } else if (element.type() == Object) {
+ // Check that the role object is valid
+ V2UserDocumentParser parser;
+ BSONObj roleObj = element.Obj();
+ Status status = parser.checkValidRoleObject(roleObj, true);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ std::string roleName;
+ std::string roleSource;
+ bool hasRole;
+ bool canDelegate;
+ status = bsonExtractStringField(roleObj, "name", &roleName);
+ if (!status.isOK()) {
+ return status;
+ }
+ status = bsonExtractStringField(roleObj, "source", &roleSource);
+ if (!status.isOK()) {
+ return status;
+ }
+ status = bsonExtractBooleanField(roleObj, "hasRole", &hasRole);
+ if (!status.isOK()) {
+ return status;
+ }
+ status = bsonExtractBooleanField(roleObj, "canDelegate", &canDelegate);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ parsedRoleData->push_back(User::RoleData(RoleName(roleName, roleSource),
+ hasRole,
+ canDelegate));
+ } else {
+ return Status(ErrorCodes::UnsupportedFormat,
+ "Values in 'roles' array must be sub-documents or strings");
+ }
+ }
+
+ return Status::OK();
+ }
+
+ Status parseRolePossessionManipulationCommands(const BSONObj& cmdObj,
+ const StringData& cmdName,
+ const StringData& rolesFieldName,
+ const std::string& dbname,
+ std::string* parsedName,
+ vector<RoleName>* parsedRoleNames,
+ BSONObj* parsedWriteConcern) {
unordered_set<std::string> validFieldNames;
validFieldNames.insert(cmdName.toString());
validFieldNames.insert("roles");
@@ -156,7 +194,7 @@ namespace auth {
if (!status.isOK()) {
return status;
}
- *parsedUserName = UserName(userNameStr, dbname);
+ *parsedName = userNameStr;
BSONElement rolesElement;
status = bsonExtractTypedField(cmdObj, "roles", Array, &rolesElement);
@@ -166,7 +204,7 @@ namespace auth {
status = _extractRoleNamesFromBSONArray(BSONArray(rolesElement.Obj()),
dbname,
- authzManager,
+ rolesFieldName,
parsedRoleNames);
if (!status.isOK()) {
return status;
@@ -253,24 +291,23 @@ namespace auth {
return Status::OK();
}
- Status parseAndValidateCreateUserCommand(const BSONObj& cmdObj,
- const std::string& dbname,
- AuthorizationManager* authzManager,
- BSONObj* parsedUserObj,
- BSONObj* parsedWriteConcern) {
+ Status parseCreateOrUpdateUserCommands(const BSONObj& cmdObj,
+ const StringData& cmdName,
+ const std::string& dbname,
+ CreateOrUpdateUserArgs* parsedArgs) {
unordered_set<std::string> validFieldNames;
- validFieldNames.insert("createUser");
+ validFieldNames.insert(cmdName.toString());
validFieldNames.insert("customData");
validFieldNames.insert("pwd");
validFieldNames.insert("roles");
validFieldNames.insert("writeConcern");
- Status status = _checkNoExtraFields(cmdObj, "createUser", validFieldNames);
+ Status status = _checkNoExtraFields(cmdObj, cmdName, validFieldNames);
if (!status.isOK()) {
return status;
}
- status = _extractWriteConcern(cmdObj, parsedWriteConcern);
+ status = _extractWriteConcern(cmdObj, &parsedArgs->writeConcern);
if (!status.isOK()) {
return status;
}
@@ -279,20 +316,12 @@ namespace auth {
// Parse user name
std::string userName;
- status = bsonExtractStringField(cmdObj, "createUser", &userName);
+ status = bsonExtractStringField(cmdObj, cmdName, &userName);
if (!status.isOK()) {
return status;
}
- // Prevent creating users in the local database
- if (dbname == "local") {
- return Status(ErrorCodes::BadValue, "Cannot create users in the local database");
- }
-
- userObjBuilder.append("_id", dbname + "." + userName);
- userObjBuilder.append(AuthorizationManager::USER_NAME_FIELD_NAME, userName);
- userObjBuilder.append(AuthorizationManager::USER_SOURCE_FIELD_NAME, dbname);
-
+ parsedArgs->userName = UserName(userName, dbname);
// Parse password
if (cmdObj.hasField("pwd")) {
@@ -302,17 +331,10 @@ namespace auth {
return status;
}
- std::string password = auth::createPasswordDigest(userName, clearTextPassword);
- userObjBuilder.append("credentials", BSON("MONGODB-CR" << password));
- } else {
- if (dbname != "$external") {
- return Status(ErrorCodes::BadValue,
- "Must provide a 'pwd' field for all user documents, except those"
- " with '$external' as the user's source");
- }
+ parsedArgs->hashedPassword = auth::createPasswordDigest(userName, clearTextPassword);
+ parsedArgs->hasHashedPassword = true;
}
-
// Parse custom data
if (cmdObj.hasField("customData")) {
BSONElement element;
@@ -320,7 +342,8 @@ namespace auth {
if (!status.isOK()) {
return status;
}
- userObjBuilder.append("customData", element.Obj());
+ parsedArgs->customData = element.Obj();
+ parsedArgs->hasCustomData = true;
}
// Parse roles
@@ -330,116 +353,13 @@ namespace auth {
if (!status.isOK()) {
return status;
}
- BSONArray modifiedRolesArray;
- status = _validateAndModifyRolesArray(rolesElement,
- dbname,
- authzManager,
- true,
- &modifiedRolesArray);
- if (!status.isOK()) {
- return status;
- }
-
- userObjBuilder.append("roles", modifiedRolesArray);
- }
-
- *parsedUserObj = userObjBuilder.obj();
-
- // Make sure document to insert is valid
- V2UserDocumentParser parser;
- status = parser.checkValidUserDocument(*parsedUserObj);
- if (!status.isOK()) {
- return status;
- }
-
- return Status::OK();
- }
-
-
- Status parseAndValidateUpdateUserCommand(const BSONObj& cmdObj,
- const std::string& dbname,
- AuthorizationManager* authzManager,
- BSONObj* parsedUpdateObj,
- UserName* parsedUserName,
- BSONObj* parsedWriteConcern) {
- unordered_set<std::string> validFieldNames;
- validFieldNames.insert("updateUser");
- validFieldNames.insert("customData");
- validFieldNames.insert("pwd");
- validFieldNames.insert("roles");
- validFieldNames.insert("writeConcern");
-
- Status status = _checkNoExtraFields(cmdObj, "updateUser", validFieldNames);
- if (!status.isOK()) {
- return status;
- }
-
- status = _extractWriteConcern(cmdObj, parsedWriteConcern);
- if (!status.isOK()) {
- return status;
- }
-
- BSONObjBuilder updateSetBuilder;
-
- // Parse user name
- std::string userName;
- status = bsonExtractStringField(cmdObj, "updateUser", &userName);
- if (!status.isOK()) {
- return status;
- }
- *parsedUserName = UserName(userName, dbname);
-
- // Parse password
- if (cmdObj.hasField("pwd")) {
- std::string clearTextPassword;
- status = bsonExtractStringField(cmdObj, "pwd", &clearTextPassword);
- if (!status.isOK()) {
- return status;
- }
-
- std::string password = auth::createPasswordDigest(userName, clearTextPassword);
- updateSetBuilder.append("credentials.MONGODB-CR", password);
- }
-
-
- // Parse custom data
- if (cmdObj.hasField("customData")) {
- BSONElement element;
- status = bsonExtractTypedField(cmdObj, "customData", Object, &element);
- if (!status.isOK()) {
- return status;
- }
- updateSetBuilder.append("customData", element.Obj());
- }
-
- // Parse roles
- if (cmdObj.hasField("roles")) {
- BSONElement rolesElement;
- Status status = bsonExtractTypedField(cmdObj, "roles", Array, &rolesElement);
- if (!status.isOK()) {
- return status;
- }
-
- BSONArray modifiedRolesObj;
- status = _validateAndModifyRolesArray(rolesElement,
- dbname,
- authzManager,
- true,
- &modifiedRolesObj);
+ status = _extractRoleDataFromBSONArray(rolesElement, dbname, &parsedArgs->roles);
if (!status.isOK()) {
return status;
}
-
- updateSetBuilder.append("roles", modifiedRolesObj);
+ parsedArgs->hasRoles = true;
}
- BSONObj updateSet = updateSetBuilder.obj();
- if (updateSet.isEmpty()) {
- return Status(ErrorCodes::UserModificationFailed,
- "Must specify at least one field to update in updateUser");
- }
-
- *parsedUpdateObj = BSON("$set" << updateSet);
return Status::OK();
}
@@ -559,82 +479,69 @@ namespace auth {
return Status(ErrorCodes::FailedToParse, errmsg);
}
- if (parsedPrivileges) {
- parsedPrivileges->push_back(privilege);
- }
+ parsedPrivileges->push_back(privilege);
}
return Status::OK();
}
- Status parseAndValidateCreateRoleCommand(const BSONObj& cmdObj,
- const std::string& dbname,
- AuthorizationManager* authzManager,
- BSONObj* parsedRoleObj,
- BSONObj* parsedWriteConcern) {
+ Status parseCreateOrUpdateRoleCommands(const BSONObj& cmdObj,
+ const StringData& cmdName,
+ const std::string& dbname,
+ CreateOrUpdateRoleArgs* parsedArgs) {
unordered_set<std::string> validFieldNames;
- validFieldNames.insert("createRole");
+ validFieldNames.insert(cmdName.toString());
validFieldNames.insert("privileges");
validFieldNames.insert("roles");
validFieldNames.insert("writeConcern");
- Status status = _checkNoExtraFields(cmdObj, "createRole", validFieldNames);
+ Status status = _checkNoExtraFields(cmdObj, cmdName, validFieldNames);
if (!status.isOK()) {
return status;
}
- status = _extractWriteConcern(cmdObj, parsedWriteConcern);
+ status = _extractWriteConcern(cmdObj, &parsedArgs->writeConcern);
if (!status.isOK()) {
return status;
}
- BSONObjBuilder roleObjBuilder;
-
- // Parse role name
std::string roleName;
status = bsonExtractStringField(cmdObj, "createRole", &roleName);
if (!status.isOK()) {
return status;
}
-
- // Prevent creating roles in the local database
- if (dbname == "local") {
- return Status(ErrorCodes::BadValue, "Cannot create roles in the local database");
- }
-
- roleObjBuilder.append("_id", dbname + "." + roleName);
- roleObjBuilder.append(AuthorizationManager::ROLE_NAME_FIELD_NAME, roleName);
- roleObjBuilder.append(AuthorizationManager::ROLE_SOURCE_FIELD_NAME, dbname);
+ parsedArgs->roleName = RoleName(roleName, dbname);
// Parse privileges
- BSONElement privilegesElement;
- status = bsonExtractTypedField(cmdObj, "privileges", Array, &privilegesElement);
- if (!status.isOK()) {
- return status;
- }
- status = _parseAndValidatePrivilegeArray(BSONArray(privilegesElement.Obj()), NULL);
- if (!status.isOK()) {
- return status;
+ if (cmdObj.hasField("privileges")) {
+ BSONElement privilegesElement;
+ status = bsonExtractTypedField(cmdObj, "privileges", Array, &privilegesElement);
+ if (!status.isOK()) {
+ return status;
+ }
+ status = _parseAndValidatePrivilegeArray(BSONArray(privilegesElement.Obj()),
+ &parsedArgs->privileges);
+ if (!status.isOK()) {
+ return status;
+ }
+ parsedArgs->hasPrivileges = true;
}
- roleObjBuilder.append(privilegesElement);
// Parse roles
- BSONElement rolesElement;
- status = bsonExtractTypedField(cmdObj, "roles", Array, &rolesElement);
- if (!status.isOK()) {
- return status;
- }
- BSONArray modifiedRolesArray;
- status = _validateAndModifyRolesArray(rolesElement,
- dbname,
- authzManager,
- false,
- &modifiedRolesArray);
- if (!status.isOK()) {
- return status;
+ if (cmdObj.hasField("roles")) {
+ BSONElement rolesElement;
+ status = bsonExtractTypedField(cmdObj, "roles", Array, &rolesElement);
+ if (!status.isOK()) {
+ return status;
+ }
+ status = _extractRoleNamesFromBSONArray(BSONArray(rolesElement.Obj()),
+ dbname,
+ "roles",
+ &parsedArgs->roles);
+ if (!status.isOK()) {
+ return status;
+ }
+ parsedArgs->hasRoles = true;
}
- roleObjBuilder.append("roles", modifiedRolesArray);
-
- *parsedRoleObj = roleObjBuilder.obj();
return Status::OK();
}
diff --git a/src/mongo/db/auth/user_management_commands_parser.h b/src/mongo/db/auth/user_management_commands_parser.h
index 9407f004514..d1bca97c5ad 100644
--- a/src/mongo/db/auth/user_management_commands_parser.h
+++ b/src/mongo/db/auth/user_management_commands_parser.h
@@ -34,6 +34,7 @@
#include "mongo/base/disallow_copying.h"
#include "mongo/db/auth/privilege.h"
#include "mongo/db/auth/role_name.h"
+#include "mongo/db/auth/user.h"
#include "mongo/db/auth/user_name.h"
#include "mongo/db/jsobj.h"
@@ -43,31 +44,28 @@ namespace mongo {
namespace auth {
- /**
- * Takes a command object describing an invocation of the "createUser" command on the database
- * "dbname", and returns (via the output param "parsedUserObj") a user object that can be
- * inserted into admin.system.users to create the user as described by the command object.
- * Also validates the input and returns a non-ok Status if there is anything wrong.
- */
- Status parseAndValidateCreateUserCommand(const BSONObj& cmdObj,
- const std::string& dbname,
- AuthorizationManager* authzManager,
- BSONObj* parsedUserObj,
- BSONObj* parsedWriteConcern);
+ struct CreateOrUpdateUserArgs {
+ UserName userName;
+ bool hasHashedPassword;
+ std::string hashedPassword;
+ bool hasCustomData;
+ BSONObj customData;
+ bool hasRoles;
+ std::vector<User::RoleData> roles;
+ BSONObj writeConcern;
+ CreateOrUpdateUserArgs() :
+ hasHashedPassword(false), hasCustomData(false), hasRoles(false) {}
+ };
/**
- * Takes a command object describing an invocation of the "updateUser" command on the database
- * "dbname", and returns (via the output param "parsedUpdateObj") an update specifier that can
- * be used to update the user document in admin.system.users as described by the command object,
- * as well as the user name of the user being updated (via the "parsedUserName" output param).
- * Also validates the input and returns a non-ok Status if there is anything wrong.
+ * Takes a command object describing an invocation of the "createUser" or "updateUser" commands
+ * (which command it is is specified in "cmdName") on the database "dbname", and parses out all
+ * the arguments into the "parsedArgs" output param.
*/
- Status parseAndValidateUpdateUserCommand(const BSONObj& cmdObj,
- const std::string& dbname,
- AuthorizationManager* authzManager,
- BSONObj* parsedUpdateObj,
- UserName* parsedUserName,
- BSONObj* parsedWriteConcern);
+ Status parseCreateOrUpdateUserCommands(const BSONObj& cmdObj,
+ const StringData& cmdName,
+ const std::string& dbname,
+ CreateOrUpdateUserArgs* parsedArgs);
/**
* Takes a command object describing an invocation of one of "grantRolesToUser",
@@ -75,13 +73,13 @@ namespace auth {
* command it is is specified in the "cmdName" argument), and parses out the user name of the
* user being modified, the roles being granted or revoked, and the write concern to use.
*/
- Status parseUserRoleManipulationCommand(const BSONObj& cmdObj,
- const StringData& cmdName,
- const std::string& dbname,
- AuthorizationManager* authzManager,
- UserName* parsedUserName,
- vector<RoleName>* parsedRoleNames,
- BSONObj* parsedWriteConcern);
+ Status parseRolePossessionManipulationCommands(const BSONObj& cmdObj,
+ const StringData& cmdName,
+ const StringData& rolesFieldName,
+ const std::string& dbname,
+ std::string* parsedName,
+ vector<RoleName>* parsedRoleNames,
+ BSONObj* parsedWriteConcern);
/**
* Takes a command object describing an invocation of the "removeUser" command and parses out
@@ -113,17 +111,26 @@ namespace auth {
const std::string& dbname,
bool* parsedAnyDb,
BSONElement* parsedNameFilter);
+
+ struct CreateOrUpdateRoleArgs {
+ RoleName roleName;
+ bool hasRoles;
+ std::vector<RoleName> roles;
+ bool hasPrivileges;
+ PrivilegeVector privileges;
+ BSONObj writeConcern;
+ CreateOrUpdateRoleArgs() : hasRoles(false), hasPrivileges(false) {}
+ };
+
/**
- * Takes a command object describing an invocation of the "createRole" command on the database
- * "dbname", and returns (via the output param "parsedRoleObj") a role object that can be
- * inserted into admin.system.roles to create the role as described by the command object.
- * Also validates the input and returns a non-ok Status if there is anything wrong.
+ * Takes a command object describing an invocation of the "createRole" or "updateRole" commands
+ * (which command it is is specified in "cmdName") on the database "dbname", and parses out all
+ * the arguments into the "parsedArgs" output param.
*/
- Status parseAndValidateCreateRoleCommand(const BSONObj& cmdObj,
- const std::string& dbname,
- AuthorizationManager* authzManager,
- BSONObj* parsedRoleObj,
- BSONObj* parsedWriteConcern);
+ Status parseCreateOrUpdateRoleCommands(const BSONObj& cmdObj,
+ const StringData& cmdName,
+ const std::string& dbname,
+ CreateOrUpdateRoleArgs* parsedArgs);
/**
* Takes a command object describing an invocation of the "grantPrivilegesToRole" or
diff --git a/src/mongo/db/auth/user_management_commands_parser_test.cpp b/src/mongo/db/auth/user_management_commands_parser_test.cpp
deleted file mode 100644
index 84f516aef2c..00000000000
--- a/src/mongo/db/auth/user_management_commands_parser_test.cpp
+++ /dev/null
@@ -1,785 +0,0 @@
-/* Copyright 2013 10gen Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * Unit tests of the functions used for parsing the commands used for user management.
- */
-
-#include <vector>
-
-#include "mongo/base/status.h"
-#include "mongo/client/auth_helpers.h"
-#include "mongo/db/auth/authz_manager_external_state_mock.h"
-#include "mongo/db/auth/authorization_manager.h"
-#include "mongo/db/auth/user_management_commands_parser.h"
-#include "mongo/db/jsobj.h"
-#include "mongo/db/server_options.h"
-#include "mongo/unittest/unittest.h"
-
-namespace mongo {
-
- // Crutches to make the test compile
- ServerGlobalParams serverGlobalParams;
- bool inShutdown() {
- return false;
- }
-
-namespace {
-
- class UserManagementCommandsParserTest : public ::mongo::unittest::Test {
- public:
- scoped_ptr<AuthorizationManager> authzManager;
- AuthzManagerExternalStateMock* externalState;
- void setUp() {
- externalState = new AuthzManagerExternalStateMock();
- authzManager.reset(new AuthorizationManager(externalState));
- }
- };
-
- TEST_F(UserManagementCommandsParserTest, WriteConcernParsing) {
- BSONObj writeConcern;
- BSONObj parsedUserObj;
- // Test no write concern provided
- ASSERT_OK(auth::parseAndValidateCreateUserCommand(BSON("createUser" << "spencer" <<
- "pwd" << "abc" <<
- "roles" << BSONArray()),
- "test",
- authzManager.get(),
- &parsedUserObj,
- &writeConcern));
- ASSERT(writeConcern.isEmpty());
-
- ASSERT_OK(auth::parseAndValidateCreateUserCommand(
- BSON("createUser" << "spencer" <<
- "pwd" << "abc" <<
- "roles" << BSONArray() <<
- "writeConcern" << BSON("w" << "majority" << "j" << true)),
- "test",
- authzManager.get(),
- &parsedUserObj,
- &writeConcern));
-
- ASSERT_EQUALS("majority", writeConcern["w"].str());
- ASSERT_EQUALS(true, writeConcern["j"].Bool());
- }
-
- TEST_F(UserManagementCommandsParserTest, CreateUserCommandParsing) {
- BSONArray emptyArray = BSONArrayBuilder().arr();
- BSONObj parsedUserObj;
- BSONObj writeConcern;
-
- // Must have password
- ASSERT_NOT_OK(auth::parseAndValidateCreateUserCommand(BSON("createUser" << "spencer" <<
- "roles" << emptyArray),
- "test",
- authzManager.get(),
- &parsedUserObj,
- &writeConcern));
-
- // Must have roles array
- ASSERT_NOT_OK(auth::parseAndValidateCreateUserCommand(BSON("createUser" << "spencer" <<
- "pwd" << "password"),
- "test",
- authzManager.get(),
- &parsedUserObj,
- &writeConcern));
-
- // Cannot create users in the local db
- ASSERT_NOT_OK(auth::parseAndValidateCreateUserCommand(BSON("createUser" << "spencer" <<
- "pwd" << "password" <<
- "roles" << emptyArray),
- "local",
- authzManager.get(),
- &parsedUserObj,
- &writeConcern));
-
- // Cannot have extra fields
- ASSERT_NOT_OK(auth::parseAndValidateCreateUserCommand(BSON("createUser" << "spencer" <<
- "pwd" << "password" <<
- "roles" << emptyArray <<
- "anotherField" << "garbage"),
- "test",
- authzManager.get(),
- &parsedUserObj,
- &writeConcern));
-
- // Role must exist (string role)
- ASSERT_NOT_OK(auth::parseAndValidateCreateUserCommand(
- BSON("createUser" << "spencer" <<
- "pwd" << "password" <<
- "roles" << BSON_ARRAY("fakeRole")),
- "test",
- authzManager.get(),
- &parsedUserObj,
- &writeConcern));
-
- // Role must exist (object role)
- ASSERT_NOT_OK(auth::parseAndValidateCreateUserCommand(
- BSON("createUser" << "spencer" <<
- "pwd" << "password" <<
- "roles" << BSON_ARRAY(BSON("name" << "fakeRole" <<
- "source" << "test" <<
- "hasRole" << true <<
- "canDelegate" << true))),
- "test",
- authzManager.get(),
- &parsedUserObj,
- &writeConcern));
-
- // Role must have name
- ASSERT_NOT_OK(auth::parseAndValidateCreateUserCommand(
- BSON("createUser" << "spencer" <<
- "pwd" << "password" <<
- "roles" << BSON_ARRAY(BSON("source" << "test" <<
- "hasRole" << true <<
- "canDelegate" << true))),
- "test",
- authzManager.get(),
- &parsedUserObj,
- &writeConcern));
-
- // Role must have source
- ASSERT_NOT_OK(auth::parseAndValidateCreateUserCommand(
- BSON("createUser" << "spencer" <<
- "pwd" << "password" <<
- "roles" << BSON_ARRAY(BSON("name" << "read" <<
- "hasRole" << true <<
- "canDelegate" << true))),
- "test",
- authzManager.get(),
- &parsedUserObj,
- &writeConcern));
-
- // Role must have hasRole
- ASSERT_NOT_OK(auth::parseAndValidateCreateUserCommand(
- BSON("createUser" << "spencer" <<
- "pwd" << "password" <<
- "roles" << BSON_ARRAY(BSON("name" << "read" <<
- "source" << "test" <<
- "canDelegate" << true))),
- "test",
- authzManager.get(),
- &parsedUserObj,
- &writeConcern));
-
- // Role must have canDelegate
- ASSERT_NOT_OK(auth::parseAndValidateCreateUserCommand(
- BSON("createUser" << "spencer" <<
- "pwd" << "password" <<
- "roles" << BSON_ARRAY(BSON("name" << "read" <<
- "source" << "test" <<
- "hasRole" << true))),
- "test",
- authzManager.get(),
- &parsedUserObj,
- &writeConcern));
-
- // canDelegate and hasRole can't both be false
- ASSERT_NOT_OK(auth::parseAndValidateCreateUserCommand(
- BSON("createUser" << "spencer" <<
- "pwd" << "password" <<
- "roles" << BSON_ARRAY(BSON("name" << "read" <<
- "source" << "test" <<
- "hasRole" << false <<
- "canDelegate" << false))),
- "test",
- authzManager.get(),
- &parsedUserObj,
- &writeConcern));
-
- // Empty roles array OK
- ASSERT_OK(auth::parseAndValidateCreateUserCommand(BSON("createUser" << "spencer" <<
- "pwd" << "password" <<
- "roles" << emptyArray),
- "test",
- authzManager.get(),
- &parsedUserObj,
- &writeConcern));
-
-
- // Missing password OK if source is $external
- ASSERT_OK(auth::parseAndValidateCreateUserCommand(BSON("createUser" << "spencer" <<
- "roles" << emptyArray),
- "$external",
- authzManager.get(),
- &parsedUserObj,
- &writeConcern));
-
- // String role names OK
- ASSERT_OK(auth::parseAndValidateCreateUserCommand(
- BSON("createUser" << "spencer" <<
- "pwd" << "password" <<
- "roles" << BSON_ARRAY("read" << "dbAdmin")),
- "test",
- authzManager.get(),
- &parsedUserObj,
- &writeConcern));
-
- ASSERT_EQUALS("spencer", parsedUserObj["name"].String());
- ASSERT_EQUALS("test", parsedUserObj["source"].String());
- std::string hashedPassword = auth::createPasswordDigest("spencer", "password");
- ASSERT_EQUALS(hashedPassword, parsedUserObj["credentials"].Obj()["MONGODB-CR"].String());
- std::vector<BSONElement> rolesArray = parsedUserObj["roles"].Array();
- ASSERT_EQUALS((size_t)2, rolesArray.size());
- ASSERT_EQUALS("read", rolesArray[0].Obj()["name"].String());
- ASSERT_EQUALS("test", rolesArray[0].Obj()["source"].String());
- ASSERT_EQUALS(true, rolesArray[0].Obj()["hasRole"].Bool());
- ASSERT_EQUALS(false, rolesArray[0].Obj()["canDelegate"].Bool());
- ASSERT_EQUALS("dbAdmin", rolesArray[1].Obj()["name"].String());
- ASSERT_EQUALS("test", rolesArray[1].Obj()["source"].String());
- ASSERT_EQUALS(true, rolesArray[1].Obj()["hasRole"].Bool());
- ASSERT_EQUALS(false, rolesArray[1].Obj()["canDelegate"].Bool());
-
-
- // Basic valid createUser command OK
- ASSERT_OK(auth::parseAndValidateCreateUserCommand(
- BSON("createUser" << "spencer" <<
- "pwd" << "password" <<
- "roles" << BSON_ARRAY(BSON("name" << "read" <<
- "source" << "test" <<
- "hasRole" << true <<
- "canDelegate" << false) <<
- BSON("name" << "dbAdminAnyDatabase" <<
- "source" << "admin" <<
- "hasRole" << false <<
- "canDelegate" << true)) <<
- "customData" << BSON("foo" << "bar")),
- "test",
- authzManager.get(),
- &parsedUserObj,
- &writeConcern));
-
- ASSERT_EQUALS("spencer", parsedUserObj["name"].String());
- ASSERT_EQUALS("test", parsedUserObj["source"].String());
- hashedPassword = auth::createPasswordDigest("spencer", "password");
- ASSERT_EQUALS(hashedPassword, parsedUserObj["credentials"].Obj()["MONGODB-CR"].String());
- ASSERT_EQUALS("bar", parsedUserObj["customData"].Obj()["foo"].String());
- rolesArray = parsedUserObj["roles"].Array();
- ASSERT_EQUALS((size_t)2, rolesArray.size());
- ASSERT_EQUALS("read", rolesArray[0].Obj()["name"].String());
- ASSERT_EQUALS("test", rolesArray[0].Obj()["source"].String());
- ASSERT_EQUALS(true, rolesArray[0].Obj()["hasRole"].Bool());
- ASSERT_EQUALS(false, rolesArray[0].Obj()["canDelegate"].Bool());
- ASSERT_EQUALS("dbAdminAnyDatabase", rolesArray[1].Obj()["name"].String());
- ASSERT_EQUALS("admin", rolesArray[1].Obj()["source"].String());
- ASSERT_EQUALS(false, rolesArray[1].Obj()["hasRole"].Bool());
- ASSERT_EQUALS(true, rolesArray[1].Obj()["canDelegate"].Bool());
-
- }
-
-
- TEST_F(UserManagementCommandsParserTest, UpdateUserCommandParsing) {
- BSONArray emptyArray = BSONArrayBuilder().arr();
- BSONObj parsedUpdateObj;
- BSONObj writeConcern;
- UserName parsedUserName;
-
- // Cannot have extra fields
- ASSERT_NOT_OK(auth::parseAndValidateUpdateUserCommand(BSON("updateUser" << "spencer" <<
- "pwd" << "password" <<
- "roles" << emptyArray <<
- "anotherField" << "garbage"),
- "test",
- authzManager.get(),
- &parsedUpdateObj,
- &parsedUserName,
- &writeConcern));
-
- // Role must exist (string role)
- ASSERT_NOT_OK(auth::parseAndValidateUpdateUserCommand(
- BSON("updateUser" << "spencer" <<
- "pwd" << "password" <<
- "roles" << BSON_ARRAY("fakeRole")),
- "test",
- authzManager.get(),
- &parsedUpdateObj,
- &parsedUserName,
- &writeConcern));
-
- // Role must exist (object role)
- ASSERT_NOT_OK(auth::parseAndValidateUpdateUserCommand(
- BSON("updateUser" << "spencer" <<
- "pwd" << "password" <<
- "roles" << BSON_ARRAY(BSON("name" << "fakeRole" <<
- "source" << "test" <<
- "hasRole" << true <<
- "canDelegate" << true))),
- "test",
- authzManager.get(),
- &parsedUpdateObj,
- &parsedUserName,
- &writeConcern));
-
- // Role must have name
- ASSERT_NOT_OK(auth::parseAndValidateUpdateUserCommand(
- BSON("updateUser" << "spencer" <<
- "pwd" << "password" <<
- "roles" << BSON_ARRAY(BSON("source" << "test" <<
- "hasRole" << true <<
- "canDelegate" << true))),
- "test",
- authzManager.get(),
- &parsedUpdateObj,
- &parsedUserName,
- &writeConcern));
-
- // Role must have source
- ASSERT_NOT_OK(auth::parseAndValidateUpdateUserCommand(
- BSON("updateUser" << "spencer" <<
- "pwd" << "password" <<
- "roles" << BSON_ARRAY(BSON("name" << "read" <<
- "hasRole" << true <<
- "canDelegate" << true))),
- "test",
- authzManager.get(),
- &parsedUpdateObj,
- &parsedUserName,
- &writeConcern));
-
- // Role must have hasRole
- ASSERT_NOT_OK(auth::parseAndValidateUpdateUserCommand(
- BSON("updateUser" << "spencer" <<
- "pwd" << "password" <<
- "roles" << BSON_ARRAY(BSON("name" << "read" <<
- "source" << "test" <<
- "canDelegate" << true))),
- "test",
- authzManager.get(),
- &parsedUpdateObj,
- &parsedUserName,
- &writeConcern));
-
- // Role must have canDelegate
- ASSERT_NOT_OK(auth::parseAndValidateUpdateUserCommand(
- BSON("updateUser" << "spencer" <<
- "pwd" << "password" <<
- "roles" << BSON_ARRAY(BSON("name" << "read" <<
- "source" << "test" <<
- "hasRole" << true))),
- "test",
- authzManager.get(),
- &parsedUpdateObj,
- &parsedUserName,
- &writeConcern));
-
- // canDelegate and hasRole can't both be false
- ASSERT_NOT_OK(auth::parseAndValidateUpdateUserCommand(
- BSON("updateUser" << "spencer" <<
- "pwd" << "password" <<
- "roles" << BSON_ARRAY(BSON("name" << "read" <<
- "source" << "test" <<
- "hasRole" << false <<
- "canDelegate" << false))),
- "test",
- authzManager.get(),
- &parsedUpdateObj,
- &parsedUserName,
- &writeConcern));
-
- // Empty roles array OK
- ASSERT_OK(auth::parseAndValidateUpdateUserCommand(BSON("updateUser" << "spencer" <<
- "pwd" << "password" <<
- "roles" << emptyArray),
- "test",
- authzManager.get(),
- &parsedUpdateObj,
- &parsedUserName,
- &writeConcern));
-
- // String role names OK
- ASSERT_OK(auth::parseAndValidateUpdateUserCommand(
- BSON("updateUser" << "spencer" <<
- "pwd" << "password" <<
- "roles" << BSON_ARRAY("read" << "dbAdmin")),
- "test",
- authzManager.get(),
- &parsedUpdateObj,
- &parsedUserName,
- &writeConcern));
-
- BSONObj setObj = parsedUpdateObj["$set"].Obj();
-
- ASSERT_EQUALS(UserName("spencer", "test"), parsedUserName);
- std::string hashedPassword = auth::createPasswordDigest("spencer", "password");
- ASSERT_EQUALS(hashedPassword, setObj["credentials.MONGODB-CR"].String());
- std::vector<BSONElement> rolesArray = setObj["roles"].Array();
- ASSERT_EQUALS((size_t)2, rolesArray.size());
- ASSERT_EQUALS("read", rolesArray[0].Obj()["name"].String());
- ASSERT_EQUALS("test", rolesArray[0].Obj()["source"].String());
- ASSERT_EQUALS(true, rolesArray[0].Obj()["hasRole"].Bool());
- ASSERT_EQUALS(false, rolesArray[0].Obj()["canDelegate"].Bool());
- ASSERT_EQUALS("dbAdmin", rolesArray[1].Obj()["name"].String());
- ASSERT_EQUALS("test", rolesArray[1].Obj()["source"].String());
- ASSERT_EQUALS(true, rolesArray[1].Obj()["hasRole"].Bool());
- ASSERT_EQUALS(false, rolesArray[1].Obj()["canDelegate"].Bool());
-
-
- // Basic valid updateUser command OK
- ASSERT_OK(auth::parseAndValidateUpdateUserCommand(
- BSON("updateUser" << "spencer" <<
- "pwd" << "password" <<
- "roles" << BSON_ARRAY(BSON("name" << "read" <<
- "source" << "test" <<
- "hasRole" << true <<
- "canDelegate" << false) <<
- BSON("name" << "dbAdminAnyDatabase" <<
- "source" << "admin" <<
- "hasRole" << false <<
- "canDelegate" << true)) <<
- "customData" << BSON("foo" << "bar")),
- "test",
- authzManager.get(),
- &parsedUpdateObj,
- &parsedUserName,
- &writeConcern));
-
- setObj = parsedUpdateObj["$set"].Obj();
-
- ASSERT_EQUALS(UserName("spencer", "test"), parsedUserName);
- hashedPassword = auth::createPasswordDigest("spencer", "password");
- ASSERT_EQUALS(hashedPassword, setObj["credentials.MONGODB-CR"].String());
- ASSERT_EQUALS("bar", setObj["customData"].Obj()["foo"].String());
- rolesArray = setObj["roles"].Array();
- ASSERT_EQUALS((size_t)2, rolesArray.size());
- ASSERT_EQUALS("read", rolesArray[0].Obj()["name"].String());
- ASSERT_EQUALS("test", rolesArray[0].Obj()["source"].String());
- ASSERT_EQUALS(true, rolesArray[0].Obj()["hasRole"].Bool());
- ASSERT_EQUALS(false, rolesArray[0].Obj()["canDelegate"].Bool());
- ASSERT_EQUALS("dbAdminAnyDatabase", rolesArray[1].Obj()["name"].String());
- ASSERT_EQUALS("admin", rolesArray[1].Obj()["source"].String());
- ASSERT_EQUALS(false, rolesArray[1].Obj()["hasRole"].Bool());
- ASSERT_EQUALS(true, rolesArray[1].Obj()["canDelegate"].Bool());
- }
-
- TEST_F(UserManagementCommandsParserTest, UserRoleManipulationCommandsParsing) {
- UserName userName;
- std::vector<RoleName> roles;
- BSONObj writeConcern;
-
- // Command name must match
- ASSERT_NOT_OK(auth::parseUserRoleManipulationCommand(
- BSON("grantRolesToUser" << "spencer" <<
- "roles" << BSON_ARRAY("read")),
- "revokeRolesFromUser",
- "test",
- authzManager.get(),
- &userName,
- &roles,
- &writeConcern));
-
- // Roles array can't be empty
- ASSERT_NOT_OK(auth::parseUserRoleManipulationCommand(
- BSON("grantRolesToUser" << "spencer" <<
- "roles" << BSONArray()),
- "grantRolesToUser",
- "test",
- authzManager.get(),
- &userName,
- &roles,
- &writeConcern));
-
- // Roles must exist
- ASSERT_NOT_OK(auth::parseUserRoleManipulationCommand(
- BSON("grantRolesToUser" << "spencer" <<
- "roles" << BSON_ARRAY("fakeRole")),
- "grantRolesToUser",
- "test",
- authzManager.get(),
- &userName,
- &roles,
- &writeConcern));
-
- ASSERT_OK(auth::parseUserRoleManipulationCommand(
- BSON("grantRolesToUser" << "spencer" <<
- "roles" << BSON_ARRAY("readWrite" << BSON("name" << "dbAdmin" <<
- "source" << "test2")) <<
- "writeConcern" << BSON("w" << 1)),
- "grantRolesToUser",
- "test",
- authzManager.get(),
- &userName,
- &roles,
- &writeConcern));
-
- ASSERT_EQUALS(UserName("spencer", "test"), userName);
- ASSERT_EQUALS(1, writeConcern["w"].numberInt());
- ASSERT_EQUALS(2U, roles.size());
- ASSERT_EQUALS(RoleName("readWrite", "test"), roles[0]);
- ASSERT_EQUALS(RoleName("dbAdmin", "test2"), roles[1]);
- }
-
- TEST_F(UserManagementCommandsParserTest, CreateRoleCommandParsing) {
- BSONObj parsedRoleObj;
- BSONObj parsedWriteConcern;
-
-
- // Must have roles array
- ASSERT_NOT_OK(auth::parseAndValidateCreateRoleCommand(BSON("createRole" << "myRole" <<
- "privileges" << BSONArray()),
- "test",
- authzManager.get(),
- &parsedRoleObj,
- &parsedWriteConcern));
-
- // Must have privileges array
- ASSERT_NOT_OK(auth::parseAndValidateCreateRoleCommand(BSON("createRole" << "myRole" <<
- "roles" << BSONArray()),
- "test",
- authzManager.get(),
- &parsedRoleObj,
- &parsedWriteConcern));
-
- // Cannot create roles in the local db
- ASSERT_NOT_OK(auth::parseAndValidateCreateRoleCommand(BSON("createRole" << "myRole" <<
- "privileges" << BSONArray() <<
- "roles" << BSONArray()),
- "local",
- authzManager.get(),
- &parsedRoleObj,
- &parsedWriteConcern));
-
- // Cannot have extra fields
- ASSERT_NOT_OK(auth::parseAndValidateCreateRoleCommand(BSON("createRole" << "myRole" <<
- "privileges" << BSONArray() <<
- "roles" << BSONArray() <<
- "anotherField" << true),
- "test",
- authzManager.get(),
- &parsedRoleObj,
- &parsedWriteConcern));
-
- // Role must exist (string role)
- ASSERT_NOT_OK(auth::parseAndValidateCreateRoleCommand(
- BSON("createRole" << "myRole" <<
- "privileges" << BSONArray() <<
- "roles" << BSON_ARRAY("fakeRole")),
- "test",
- authzManager.get(),
- &parsedRoleObj,
- &parsedWriteConcern));
-
- // Role must exist (object role)
- ASSERT_NOT_OK(auth::parseAndValidateCreateRoleCommand(
- BSON("createRole" << "myRole" <<
- "privileges" << BSONArray() <<
- "roles" << BSON_ARRAY("name" << "fakeRole" << "source" << "test")),
- "test",
- authzManager.get(),
- &parsedRoleObj,
- &parsedWriteConcern));
-
- // Role must have name
- ASSERT_NOT_OK(auth::parseAndValidateCreateRoleCommand(
- BSON("createRole" << "myRole" <<
- "privileges" << BSONArray() <<
- "roles" << BSON_ARRAY("source" << "test")),
- "test",
- authzManager.get(),
- &parsedRoleObj,
- &parsedWriteConcern));
-
- // Role must have source
- ASSERT_NOT_OK(auth::parseAndValidateCreateRoleCommand(
- BSON("createRole" << "myRole" <<
- "privileges" << BSONArray() <<
- "roles" << BSON_ARRAY("name" << "readWrite")),
- "test",
- authzManager.get(),
- &parsedRoleObj,
- &parsedWriteConcern));
-
- // Role must not have have canDelegate or hasRole
- ASSERT_NOT_OK(auth::parseAndValidateCreateRoleCommand(
- BSON("createRole" << "myRole" <<
- "privileges" << BSONArray() <<
- "roles" << BSON_ARRAY("name" << "readWrite" <<
- "source" << "test" <<
- "hasRole" << true <<
- "canDelegate" << false)),
- "test",
- authzManager.get(),
- &parsedRoleObj,
- &parsedWriteConcern));
-
- // Privilege must have resource
- ASSERT_NOT_OK(auth::parseAndValidateCreateRoleCommand(
- BSON("createRole" << "myRole" <<
- "privileges" << BSON_ARRAY(BSON("actions" << BSON_ARRAY("find"))) <<
- "roles" << BSONArray()),
- "test",
- authzManager.get(),
- &parsedRoleObj,
- &parsedWriteConcern));
-
- // Privilege must have actions
- ASSERT_NOT_OK(auth::parseAndValidateCreateRoleCommand(
- BSON("createRole" << "myRole" <<
- "privileges" << BSON_ARRAY(BSON("resource" << BSON("cluster" << true))) <<
- "roles" << BSONArray()),
- "test",
- authzManager.get(),
- &parsedRoleObj,
- &parsedWriteConcern));
-
- // Action must exist
- ASSERT_NOT_OK(auth::parseAndValidateCreateRoleCommand(
- BSON("createRole" << "myRole" <<
- "privileges" << BSON_ARRAY(BSON("resource" << BSON("cluster" << true) <<
- "actions" << BSON_ARRAY("fakeAction"))) <<
- "roles" << BSONArray()),
- "test",
- authzManager.get(),
- &parsedRoleObj,
- &parsedWriteConcern));
-
- // Resource can't be on cluster AND normal resources
- ASSERT_NOT_OK(auth::parseAndValidateCreateRoleCommand(
- BSON("createRole" << "myRole" <<
- "privileges" << BSON_ARRAY(BSON("resource" << BSON("cluster" << true <<
- "db" << "" <<
- "collection" << "") <<
- "actions" << BSONArray())) <<
- "roles" << BSONArray()),
- "test",
- authzManager.get(),
- &parsedRoleObj,
- &parsedWriteConcern));
-
- // Resource can't have db without collection
- ASSERT_NOT_OK(auth::parseAndValidateCreateRoleCommand(
- BSON("createRole" << "myRole" <<
- "privileges" << BSON_ARRAY(BSON("resource" << BSON("db" << "" ) <<
- "actions" << BSONArray())) <<
- "roles" << BSONArray()),
- "test",
- authzManager.get(),
- &parsedRoleObj,
- &parsedWriteConcern));
-
- // Resource can't have collection without db
- ASSERT_NOT_OK(auth::parseAndValidateCreateRoleCommand(
- BSON("createRole" << "myRole" <<
- "privileges" << BSON_ARRAY(BSON("resource" << BSON("collection" << "" ) <<
- "actions" << BSONArray())) <<
- "roles" << BSONArray()),
- "test",
- authzManager.get(),
- &parsedRoleObj,
- &parsedWriteConcern));
-
- // Empty roles and privileges arrays OK
- ASSERT_OK(auth::parseAndValidateCreateRoleCommand(
- BSON("createRole" << "myRole" <<
- "privileges" << BSONArray() <<
- "roles" << BSONArray()),
- "test",
- authzManager.get(),
- &parsedRoleObj,
- &parsedWriteConcern));
-
- ASSERT_EQUALS("myRole", parsedRoleObj["name"].String());
- ASSERT_EQUALS("test", parsedRoleObj["source"].String());
- ASSERT_EQUALS(0U, parsedRoleObj["privileges"].Array().size());
- ASSERT_EQUALS(0U, parsedRoleObj["roles"].Array().size());
-
- // Empty db and collection for privilege is OK.
- ASSERT_OK(auth::parseAndValidateCreateRoleCommand(
- BSON("createRole" << "myRole" <<
- "privileges" << BSON_ARRAY(BSON("resource" << BSON("db" << "" <<
- "collection" << "" ) <<
- "actions" << BSON_ARRAY("find"))) <<
- "roles" << BSONArray()),
- "test",
- authzManager.get(),
- &parsedRoleObj,
- &parsedWriteConcern));
-
- std::vector<BSONElement> privileges = parsedRoleObj["privileges"].Array();
- ASSERT_EQUALS(1U, privileges.size());
- ASSERT_EQUALS("", privileges[0].Obj()["resource"].Obj()["db"].String());
- ASSERT_EQUALS("", privileges[0].Obj()["resource"].Obj()["collection"].String());
- ASSERT_EQUALS(1U, privileges[0].Obj()["actions"].Array().size());
- ASSERT_EQUALS("find", privileges[0].Obj()["actions"].Array()[0].String());
-
- // Explicit collection name resource OK
- ASSERT_OK(auth::parseAndValidateCreateRoleCommand(
- BSON("createRole" << "myRole" <<
- "privileges" << BSON_ARRAY(BSON("resource" << BSON("db" << "test" <<
- "collection" << "foo" ) <<
- "actions" << BSON_ARRAY("find"))) <<
- "roles" << BSONArray()),
- "test",
- authzManager.get(),
- &parsedRoleObj,
- &parsedWriteConcern));
-
- privileges = parsedRoleObj["privileges"].Array();
- ASSERT_EQUALS(1U, privileges.size());
- ASSERT_EQUALS("test", privileges[0].Obj()["resource"].Obj()["db"].String());
- ASSERT_EQUALS("foo", privileges[0].Obj()["resource"].Obj()["collection"].String());
-
- // Cluster resource OK
- ASSERT_OK(auth::parseAndValidateCreateRoleCommand(
- BSON("createRole" << "myRole" <<
- "privileges" << BSON_ARRAY(BSON("resource" << BSON("cluster" << true) <<
- "actions" << BSON_ARRAY("find"))) <<
- "roles" << BSONArray()),
- "test",
- authzManager.get(),
- &parsedRoleObj,
- &parsedWriteConcern));
-
- privileges = parsedRoleObj["privileges"].Array();
- ASSERT_EQUALS(1U, privileges.size());
- ASSERT_EQUALS(true, privileges[0].Obj()["resource"].Obj()["cluster"].Bool());
-
- // String role names OK
- ASSERT_OK(auth::parseAndValidateCreateRoleCommand(
- BSON("createRole" << "myRole" <<
- "privileges" << BSONArray() <<
- "roles" << BSON_ARRAY("read" << "dbAdmin")),
- "test",
- authzManager.get(),
- &parsedRoleObj,
- &parsedWriteConcern));
-
- std::vector<BSONElement> rolesArray = parsedRoleObj["roles"].Array();
- ASSERT_EQUALS((size_t)2, rolesArray.size());
- ASSERT_EQUALS("read", rolesArray[0].Obj()["name"].String());
- ASSERT_EQUALS("test", rolesArray[0].Obj()["source"].String());
- ASSERT_EQUALS("dbAdmin", rolesArray[1].Obj()["name"].String());
- ASSERT_EQUALS("test", rolesArray[1].Obj()["source"].String());
-
- // Object roles OK
- ASSERT_OK(auth::parseAndValidateCreateRoleCommand(
- BSON("createRole" << "myRole" <<
- "privileges" << BSONArray() <<
- "roles" << BSON_ARRAY(BSON("name" << "read" << "source" << "test") <<
- BSON("name" << "dbAdmin" << "source" << "test"))),
- "test",
- authzManager.get(),
- &parsedRoleObj,
- &parsedWriteConcern));
-
- rolesArray = parsedRoleObj["roles"].Array();
- ASSERT_EQUALS((size_t)2, rolesArray.size());
- ASSERT_EQUALS("read", rolesArray[0].Obj()["name"].String());
- ASSERT_EQUALS("test", rolesArray[0].Obj()["source"].String());
- ASSERT_EQUALS("dbAdmin", rolesArray[1].Obj()["name"].String());
- ASSERT_EQUALS("test", rolesArray[1].Obj()["source"].String());
- }
-
-} // namespace
-} // namespace mongo
diff --git a/src/mongo/db/commands/user_management_commands.cpp b/src/mongo/db/commands/user_management_commands.cpp
index 6a6d2b322e7..d778e705ac1 100644
--- a/src/mongo/db/commands/user_management_commands.cpp
+++ b/src/mongo/db/commands/user_management_commands.cpp
@@ -44,6 +44,7 @@
#include "mongo/db/auth/authorization_manager_global.h"
#include "mongo/db/auth/privilege.h"
#include "mongo/db/auth/user.h"
+#include "mongo/db/auth/user_document_parser.h"
#include "mongo/db/auth/user_management_commands_parser.h"
#include "mongo/db/commands.h"
#include "mongo/db/jsobj.h"
@@ -52,6 +53,8 @@
namespace mongo {
+ using mongoutils::str::stream;
+
static void addStatus(const Status& status, BSONObjBuilder& builder) {
builder.append("ok", status.isOK() ? 1.0: 0.0);
if (!status.isOK())
@@ -71,7 +74,7 @@ namespace mongo {
}
}
- static BSONArray rolesToBSONArray(const User::RoleDataMap& roles) {
+ static BSONArray roleDataMapToBSONArray(const User::RoleDataMap& roles) {
BSONArrayBuilder arrBuilder;
for (User::RoleDataMap::const_iterator it = roles.begin(); it != roles.end(); ++it) {
const User::RoleData& role = it->second;
@@ -83,6 +86,72 @@ namespace mongo {
return arrBuilder.arr();
}
+ // Should only be called inside the AuthzUpdateLock
+ static Status getBSONForRoleVectorIfRolesExist(const std::vector<RoleName>& roles,
+ AuthorizationManager* authzManager,
+ BSONArray* result) {
+ BSONArrayBuilder rolesArrayBuilder;
+ for (std::vector<RoleName>::const_iterator it = roles.begin(); it != roles.end(); ++it) {
+ const RoleName& role = *it;
+ if (!authzManager->roleExists(role)) {
+ return Status(ErrorCodes::RoleNotFound,
+ mongoutils::str::stream() << role.getFullName() <<
+ " does not name an existing role");
+ }
+ rolesArrayBuilder.append(BSON("name" << role.getRole() << "source" << role.getDB()));
+ }
+ *result = rolesArrayBuilder.arr();
+ return Status::OK();
+ }
+
+ // Should only be called inside the AuthzUpdateLock
+ static Status roleDataVectorToBSONArrayIfRolesExist(const std::vector<User::RoleData>& roles,
+ AuthorizationManager* authzManager,
+ BSONArray* result) {
+ BSONArrayBuilder rolesArrayBuilder;
+ for (std::vector<User::RoleData>::const_iterator it = roles.begin();
+ it != roles.end(); ++it) {
+ const User::RoleData& role = *it;
+ if (!authzManager->roleExists(role.name)) {
+ return Status(ErrorCodes::RoleNotFound,
+ mongoutils::str::stream() << it->name.getFullName() <<
+ " does not name an existing role");
+ }
+ if (!role.hasRole && !role.canDelegate) {
+ return Status(ErrorCodes::BadValue, "At least one of \"hasRole\" and "
+ "\"canDelegate\" must be true for every role object");
+ }
+ rolesArrayBuilder.append(BSON("name" << role.name.getRole() <<
+ "source" << role.name.getDB() <<
+ "hasRole" << role.hasRole <<
+ "canDelegate" << role.canDelegate));
+ }
+ *result = rolesArrayBuilder.arr();
+ return Status::OK();
+ }
+
+ static Status privilegeVectorToBSONArray(const PrivilegeVector& privileges, BSONArray* result) {
+ BSONArrayBuilder arrBuilder;
+ for (PrivilegeVector::const_iterator it = privileges.begin();
+ it != privileges.end(); ++it) {
+ const Privilege& privilege = *it;
+
+ ParsedPrivilege parsedPrivilege;
+ std::string errmsg;
+ if (!ParsedPrivilege::privilegeToParsedPrivilege(privilege,
+ &parsedPrivilege,
+ &errmsg)) {
+ return Status(ErrorCodes::FailedToParse, errmsg);
+ }
+ if (!parsedPrivilege.isValid(&errmsg)) {
+ return Status(ErrorCodes::FailedToParse, errmsg);
+ }
+ arrBuilder.append(parsedPrivilege.toBSON());
+ }
+ *result = arrBuilder.arr();
+ return Status::OK();
+ }
+
static Status getCurrentUserRoles(AuthorizationManager* authzManager,
const UserName& userName,
User::RoleDataMap* roles) {
@@ -132,6 +201,52 @@ namespace mongo {
string& errmsg,
BSONObjBuilder& result,
bool fromRepl) {
+ auth::CreateOrUpdateUserArgs args;
+ Status status = auth::parseCreateOrUpdateUserCommands(cmdObj,
+ "createUser",
+ dbname,
+ &args);
+ if (!status.isOK()) {
+ addStatus(status, result);
+ return false;
+ }
+
+ if (args.userName.getDB() == "local") {
+ addStatus(Status(ErrorCodes::BadValue, "Cannot create users in the local database"),
+ result);
+ return false;
+ }
+
+ if (!args.hasHashedPassword && args.userName.getDB() != "$external") {
+ addStatus(Status(ErrorCodes::BadValue,
+ "Must provide a 'pwd' field for all user documents, except those"
+ " with '$external' as the user's source"),
+ result);
+ return false;
+ }
+
+ if (!args.hasRoles) {
+ addStatus(Status(ErrorCodes::BadValue,
+ "\"createUser\" command requires a \"roles\" array"),
+ result);
+ return false;
+ }
+
+ BSONObjBuilder userObjBuilder;
+ userObjBuilder.append("_id",
+ stream() << args.userName.getDB() << "." <<
+ args.userName.getUser());
+ userObjBuilder.append(AuthorizationManager::USER_NAME_FIELD_NAME,
+ args.userName.getUser());
+ userObjBuilder.append(AuthorizationManager::USER_SOURCE_FIELD_NAME,
+ args.userName.getDB());
+ if (args.hasHashedPassword) {
+ userObjBuilder.append("credentials", BSON("MONGODB-CR" << args.hashedPassword));
+ }
+ if (args.hasCustomData) {
+ userObjBuilder.append("customData", args.customData);
+ }
+
AuthorizationManager* authzManager = getGlobalAuthorizationManager();
AuthzDocumentsUpdateGuard updateGuard(authzManager);
if (!updateGuard.tryLock("Create user")) {
@@ -140,19 +255,25 @@ namespace mongo {
return false;
}
- BSONObj userObj;
- BSONObj writeConcern;
- Status status = auth::parseAndValidateCreateUserCommand(cmdObj,
- dbname,
- authzManager,
- &userObj,
- &writeConcern);
+ // Role existence has to be checked after acquiring the update lock
+ BSONArray rolesArray;
+ status = roleDataVectorToBSONArrayIfRolesExist(args.roles, authzManager, &rolesArray);
if (!status.isOK()) {
addStatus(status, result);
return false;
}
+ userObjBuilder.append("roles", rolesArray);
- status = authzManager->insertPrivilegeDocument(dbname, userObj, writeConcern);
+ BSONObj userObj = userObjBuilder.obj();
+ V2UserDocumentParser parser;
+ status = parser.checkValidUserDocument(userObj);
+ if (!status.isOK()) {
+ addStatus(status, result);
+ return false;
+ }
+ status = authzManager->insertPrivilegeDocument(dbname,
+ userObj,
+ args.writeConcern);
if (!status.isOK()) {
addStatus(status, result);
return false;
@@ -202,6 +323,31 @@ namespace mongo {
string& errmsg,
BSONObjBuilder& result,
bool fromRepl) {
+ auth::CreateOrUpdateUserArgs args;
+ Status status = auth::parseCreateOrUpdateUserCommands(cmdObj,
+ "updateUser",
+ dbname,
+ &args);
+ if (!status.isOK()) {
+ addStatus(status, result);
+ return false;
+ }
+
+ if (!args.hasHashedPassword && !args.hasCustomData && !args.hasRoles) {
+ addStatus(Status(ErrorCodes::BadValue,
+ "Must specify at least one field to update in updateUser"),
+ result);
+ return false;
+ }
+
+ BSONObjBuilder updateSetBuilder;
+ if (args.hasHashedPassword) {
+ updateSetBuilder.append("credentials.MONGODB-CR", args.hashedPassword);
+ }
+ if (args.hasCustomData) {
+ updateSetBuilder.append("customData", args.customData);
+ }
+
AuthorizationManager* authzManager = getGlobalAuthorizationManager();
AuthzDocumentsUpdateGuard updateGuard(authzManager);
if (!updateGuard.tryLock("Update user")) {
@@ -210,23 +356,24 @@ namespace mongo {
return false;
}
- BSONObj updateObj;
- UserName userName;
- BSONObj writeConcern;
- Status status = auth::parseAndValidateUpdateUserCommand(cmdObj,
- dbname,
- authzManager,
- &updateObj,
- &userName,
- &writeConcern);
- if (!status.isOK()) {
- addStatus(status, result);
- return false;
+ // Role existence has to be checked after acquiring the update lock
+ if (args.hasRoles) {
+ BSONArray rolesArray;
+ status = roleDataVectorToBSONArrayIfRolesExist(args.roles,
+ authzManager,
+ &rolesArray);
+ if (!status.isOK()) {
+ addStatus(status, result);
+ return false;
+ }
+ updateSetBuilder.append("roles", rolesArray);
}
- status = authzManager->updatePrivilegeDocument(userName, updateObj, writeConcern);
+ status = authzManager->updatePrivilegeDocument(args.userName,
+ BSON("$set" << updateSetBuilder.done()),
+ args.writeConcern);
// Must invalidate even on bad status - what if the write succeeded but the GLE failed?
- authzManager->invalidateUserByName(userName);
+ authzManager->invalidateUserByName(args.userName);
if (!status.isOK()) {
addStatus(status, result);
return false;
@@ -438,21 +585,22 @@ namespace mongo {
return false;
}
- UserName userName;
+ std::string userNameString;
std::vector<RoleName> roles;
BSONObj writeConcern;
- Status status = auth::parseUserRoleManipulationCommand(cmdObj,
- "grantRolesToUser",
- dbname,
- authzManager,
- &userName,
- &roles,
- &writeConcern);
+ Status status = auth::parseRolePossessionManipulationCommands(cmdObj,
+ "grantRolesToUser",
+ "roles",
+ dbname,
+ &userNameString,
+ &roles,
+ &writeConcern);
if (!status.isOK()) {
addStatus(status, result);
return false;
}
+ UserName userName(userNameString, dbname);
User::RoleDataMap userRoles;
status = getCurrentUserRoles(authzManager, userName, &userRoles);
if (!status.isOK()) {
@@ -462,6 +610,13 @@ namespace mongo {
for (vector<RoleName>::iterator it = roles.begin(); it != roles.end(); ++it) {
RoleName& roleName = *it;
+ if (!authzManager->roleExists(roleName)) {
+ addStatus(Status(ErrorCodes::RoleNotFound,
+ mongoutils::str::stream() << roleName.getFullName() <<
+ " does not name an existing role"),
+ result);
+ return false;
+ }
User::RoleData& role = userRoles[roleName];
if (role.name.empty()) {
role.name = roleName;
@@ -469,7 +624,7 @@ namespace mongo {
role.hasRole = true;
}
- BSONArray newRolesBSONArray = rolesToBSONArray(userRoles);
+ BSONArray newRolesBSONArray = roleDataMapToBSONArray(userRoles);
status = authzManager->updatePrivilegeDocument(
userName, BSON("$set" << BSON("roles" << newRolesBSONArray)), writeConcern);
// Must invalidate even on bad status - what if the write succeeded but the GLE failed?
@@ -528,21 +683,22 @@ namespace mongo {
return false;
}
- UserName userName;
+ std::string userNameString;
std::vector<RoleName> roles;
BSONObj writeConcern;
- Status status = auth::parseUserRoleManipulationCommand(cmdObj,
- "revokeRolesFromUser",
- dbname,
- authzManager,
- &userName,
- &roles,
- &writeConcern);
+ Status status = auth::parseRolePossessionManipulationCommands(cmdObj,
+ "revokeRolesFromUser",
+ "roles",
+ dbname,
+ &userNameString,
+ &roles,
+ &writeConcern);
if (!status.isOK()) {
addStatus(status, result);
return false;
}
+ UserName userName(userNameString, dbname);
User::RoleDataMap userRoles;
status = getCurrentUserRoles(authzManager, userName, &userRoles);
if (!status.isOK()) {
@@ -552,6 +708,13 @@ namespace mongo {
for (vector<RoleName>::iterator it = roles.begin(); it != roles.end(); ++it) {
RoleName& roleName = *it;
+ if (!authzManager->roleExists(roleName)) {
+ addStatus(Status(ErrorCodes::RoleNotFound,
+ mongoutils::str::stream() << roleName.getFullName() <<
+ " does not name an existing role"),
+ result);
+ return false;
+ }
User::RoleDataMap::iterator roleDataIt = userRoles.find(roleName);
if (roleDataIt == userRoles.end()) {
continue; // User already doesn't have the role, nothing to do
@@ -567,7 +730,7 @@ namespace mongo {
}
}
- BSONArray newRolesBSONArray = rolesToBSONArray(userRoles);
+ BSONArray newRolesBSONArray = roleDataMapToBSONArray(userRoles);
status = authzManager->updatePrivilegeDocument(
userName, BSON("$set" << BSON("roles" << newRolesBSONArray)), writeConcern);
// Must invalidate even on bad status - what if the write succeeded but the GLE failed?
@@ -626,21 +789,22 @@ namespace mongo {
return false;
}
- UserName userName;
+ std::string userNameString;
std::vector<RoleName> roles;
BSONObj writeConcern;
- Status status = auth::parseUserRoleManipulationCommand(cmdObj,
- "grantDelegateRolesToUser",
- dbname,
- authzManager,
- &userName,
- &roles,
- &writeConcern);
+ Status status = auth::parseRolePossessionManipulationCommands(cmdObj,
+ "grantDelegateRolesToUser",
+ "roles",
+ dbname,
+ &userNameString,
+ &roles,
+ &writeConcern);
if (!status.isOK()) {
addStatus(status, result);
return false;
}
+ UserName userName(userNameString, dbname);
User::RoleDataMap userRoles;
status = getCurrentUserRoles(authzManager, userName, &userRoles);
if (!status.isOK()) {
@@ -650,6 +814,13 @@ namespace mongo {
for (vector<RoleName>::iterator it = roles.begin(); it != roles.end(); ++it) {
RoleName& roleName = *it;
+ if (!authzManager->roleExists(roleName)) {
+ addStatus(Status(ErrorCodes::RoleNotFound,
+ mongoutils::str::stream() << roleName.getFullName() <<
+ " does not name an existing role"),
+ result);
+ return false;
+ }
User::RoleData& role = userRoles[roleName];
if (role.name.empty()) {
role.name = roleName;
@@ -657,7 +828,7 @@ namespace mongo {
role.canDelegate = true;
}
- BSONArray newRolesBSONArray = rolesToBSONArray(userRoles);
+ BSONArray newRolesBSONArray = roleDataMapToBSONArray(userRoles);
status = authzManager->updatePrivilegeDocument(
userName, BSON("$set" << BSON("roles" << newRolesBSONArray)), writeConcern);
// Must invalidate even on bad status - what if the write succeeded but the GLE failed?
@@ -716,21 +887,23 @@ namespace mongo {
return false;
}
- UserName userName;
+ std::string userNameString;
std::vector<RoleName> roles;
BSONObj writeConcern;
- Status status = auth::parseUserRoleManipulationCommand(cmdObj,
- "revokeDelegateRolesFromUser",
- dbname,
- authzManager,
- &userName,
- &roles,
- &writeConcern);
+ Status status =
+ auth::parseRolePossessionManipulationCommands(cmdObj,
+ "revokeDelegateRolesFromUser",
+ "roles",
+ dbname,
+ &userNameString,
+ &roles,
+ &writeConcern);
if (!status.isOK()) {
addStatus(status, result);
return false;
}
+ UserName userName(userNameString, dbname);
User::RoleDataMap userRoles;
status = getCurrentUserRoles(authzManager, userName, &userRoles);
if (!status.isOK()) {
@@ -740,6 +913,13 @@ namespace mongo {
for (vector<RoleName>::iterator it = roles.begin(); it != roles.end(); ++it) {
RoleName& roleName = *it;
+ if (!authzManager->roleExists(roleName)) {
+ addStatus(Status(ErrorCodes::RoleNotFound,
+ mongoutils::str::stream() << roleName.getFullName() <<
+ " does not name an existing role"),
+ result);
+ return false;
+ }
User::RoleDataMap::iterator roleDataIt = userRoles.find(roleName);
if (roleDataIt == userRoles.end()) {
continue; // User already doesn't have the role, nothing to do
@@ -755,7 +935,7 @@ namespace mongo {
}
}
- BSONArray newRolesBSONArray = rolesToBSONArray(userRoles);
+ BSONArray newRolesBSONArray = roleDataMapToBSONArray(userRoles);
status = authzManager->updatePrivilegeDocument(
userName, BSON("$set" << BSON("roles" << newRolesBSONArray)), writeConcern);
// Must invalidate even on bad status - what if the write succeeded but the GLE failed?
@@ -877,6 +1057,53 @@ namespace mongo {
string& errmsg,
BSONObjBuilder& result,
bool fromRepl) {
+ auth::CreateOrUpdateRoleArgs args;
+ Status status = auth::parseCreateOrUpdateRoleCommands(cmdObj,
+ "createRole",
+ dbname,
+ &args);
+ if (!status.isOK()) {
+ addStatus(status, result);
+ return false;
+ }
+
+ if (args.roleName.getDB() == "local") {
+ addStatus(Status(ErrorCodes::BadValue, "Cannot create roles in the local database"),
+ result);
+ return false;
+ }
+
+ if (!args.hasRoles) {
+ addStatus(Status(ErrorCodes::BadValue,
+ "\"createRole\" command requires a \"roles\" array"),
+ result);
+ return false;
+ }
+
+ if (!args.hasPrivileges) {
+ addStatus(Status(ErrorCodes::BadValue,
+ "\"createRole\" command requires a \"privileges\" array"),
+ result);
+ return false;
+ }
+
+ BSONObjBuilder roleObjBuilder;
+
+ roleObjBuilder.append("_id", stream() << args.roleName.getDB() << "." <<
+ args.roleName.getRole());
+ roleObjBuilder.append(AuthorizationManager::ROLE_NAME_FIELD_NAME,
+ args.roleName.getRole());
+ roleObjBuilder.append(AuthorizationManager::ROLE_SOURCE_FIELD_NAME,
+ args.roleName.getDB());
+
+ BSONArray privileges;
+ status = privilegeVectorToBSONArray(args.privileges, &privileges);
+ if (!status.isOK()) {
+ addStatus(status, result);
+ return false;
+ }
+ roleObjBuilder.append("privileges", privileges);
+
AuthorizationManager* authzManager = getGlobalAuthorizationManager();
AuthzDocumentsUpdateGuard updateGuard(authzManager);
if (!updateGuard.tryLock("Create role")) {
@@ -885,19 +1112,16 @@ namespace mongo {
return false;
}
- BSONObj roleObj;
- BSONObj writeConcern;
- Status status = auth::parseAndValidateCreateRoleCommand(cmdObj,
- dbname,
- authzManager,
- &roleObj,
- &writeConcern);
+ // Role existence has to be checked after acquiring the update lock
+ BSONArray roles;
+ status = getBSONForRoleVectorIfRolesExist(args.roles, authzManager, &roles);
if (!status.isOK()) {
addStatus(status, result);
return false;
}
+ roleObjBuilder.append("roles", roles);
- status = authzManager->insertRoleDocument(roleObj, writeConcern);
+ status = authzManager->insertRoleDocument(roleObjBuilder.done(), args.writeConcern);
if (!status.isOK()) {
addStatus(status, result);
return false;
@@ -1022,7 +1246,7 @@ namespace mongo {
return true;
}
- } cmdGrantPrivilegeToRole;
+ } cmdGrantPrivilegesToRole;
class CmdRolesInfo: public Command {
public: