summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Alabi <alabidan@gmail.com>2015-05-11 09:25:12 -0400
committerDaniel Alabi <alabidan@gmail.com>2015-05-13 16:46:17 -0400
commitfbae7c1bf914a594ce3f4e4338e2231bba872582 (patch)
tree1f18e2b0a677e36e6c546eeae0ffc8f0df5fea06
parent4be360e1d25b7c15e88a19b410493f7df68f3cbc (diff)
downloadmongo-fbae7c1bf914a594ce3f4e4338e2231bba872582.tar.gz
SERVER-18328 Create mongos version of user management commands that uses catalog manager
-rw-r--r--src/mongo/db/SConscript3
-rw-r--r--src/mongo/db/commands/user_management_commands.cpp748
-rw-r--r--src/mongo/db/commands/user_management_commands.h199
-rw-r--r--src/mongo/db/commands/user_management_commands_common.cpp749
-rw-r--r--src/mongo/s/catalog/catalog_manager.h21
-rw-r--r--src/mongo/s/catalog/legacy/catalog_manager_legacy.cpp103
-rw-r--r--src/mongo/s/catalog/legacy/catalog_manager_legacy.h12
-rw-r--r--src/mongo/s/commands/SConscript1
-rw-r--r--src/mongo/s/commands/cluster_user_management_commands.cpp791
9 files changed, 1958 insertions, 669 deletions
diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript
index a074bbbf81b..92cbb92e5fe 100644
--- a/src/mongo/db/SConscript
+++ b/src/mongo/db/SConscript
@@ -427,7 +427,7 @@ coredbEnv.Library(
"commands/rename_collection_common.cpp",
"commands/server_status.cpp",
"commands/parameters.cpp",
- "commands/user_management_commands.cpp",
+ "commands/user_management_commands_common.cpp",
"commands/write_commands/write_commands_common.cpp",
"pipeline/pipeline.cpp",
"dbcommands_generic.cpp",
@@ -547,6 +547,7 @@ serverOnlyFiles = [
"commands/test_commands.cpp",
"commands/top_command.cpp",
"commands/touch.cpp",
+ "commands/user_management_commands.cpp",
"commands/validate.cpp",
"commands/write_commands/batch_executor.cpp",
"commands/write_commands/write_commands.cpp",
diff --git a/src/mongo/db/commands/user_management_commands.cpp b/src/mongo/db/commands/user_management_commands.cpp
index d0813279a0f..fbac3ef177b 100644
--- a/src/mongo/db/commands/user_management_commands.cpp
+++ b/src/mongo/db/commands/user_management_commands.cpp
@@ -30,6 +30,8 @@
#include "mongo/platform/basic.h"
+#include "mongo/db/commands/user_management_commands.h"
+
#include <string>
#include <vector>
@@ -72,250 +74,6 @@ namespace mongo {
using std::stringstream;
using std::vector;
- static void redactPasswordData(mutablebson::Element parent) {
- namespace mmb = mutablebson;
- const StringData pwdFieldName("pwd", StringData::LiteralTag());
- for (mmb::Element pwdElement = mmb::findFirstChildNamed(parent, pwdFieldName);
- pwdElement.ok();
- pwdElement = mmb::findElementNamed(pwdElement.rightSibling(), pwdFieldName)) {
-
- pwdElement.setValueString("xxx");
- }
- }
-
- static BSONArray roleSetToBSONArray(const unordered_set<RoleName>& roles) {
- BSONArrayBuilder rolesArrayBuilder;
- for (unordered_set<RoleName>::const_iterator it = roles.begin(); it != roles.end(); ++it) {
- const RoleName& role = *it;
- rolesArrayBuilder.append(
- BSON(AuthorizationManager::ROLE_NAME_FIELD_NAME << role.getRole() <<
- AuthorizationManager::ROLE_DB_FIELD_NAME << role.getDB()));
- }
- return rolesArrayBuilder.arr();
- }
-
- static BSONArray rolesVectorToBSONArray(const std::vector<RoleName>& roles) {
- BSONArrayBuilder rolesArrayBuilder;
- for (std::vector<RoleName>::const_iterator it = roles.begin(); it != roles.end(); ++it) {
- const RoleName& role = *it;
- rolesArrayBuilder.append(
- BSON(AuthorizationManager::ROLE_NAME_FIELD_NAME << role.getRole() <<
- AuthorizationManager::ROLE_DB_FIELD_NAME << role.getDB()));
- }
- return rolesArrayBuilder.arr();
- }
-
- 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(OperationContext* txn,
- AuthorizationManager* authzManager,
- const UserName& userName,
- unordered_set<RoleName>* roles) {
- User* user;
- authzManager->invalidateUserByName(userName); // Need to make sure cache entry is up to date
- Status status = authzManager->acquireUser(txn, userName, &user);
- if (!status.isOK()) {
- return status;
- }
- RoleNameIterator rolesIt = user->getRoles();
- while (rolesIt.more()) {
- roles->insert(rolesIt.next());
- }
- authzManager->releaseUser(user);
- return Status::OK();
- }
-
- static Status checkAuthorizedToGrantRoles(AuthorizationSession* authzSession,
- const std::vector<RoleName>& roles) {
- for (size_t i = 0; i < roles.size(); ++i) {
- if (!authzSession->isAuthorizedToGrantRole(roles[i])) {
- return Status(ErrorCodes::Unauthorized,
- str::stream() << "Not authorized to grant role: " <<
- roles[i].getFullName());
- }
- }
- return Status::OK();
- }
-
- static Status checkAuthorizedToRevokeRoles(AuthorizationSession* authzSession,
- const std::vector<RoleName>& roles) {
- for (size_t i = 0; i < roles.size(); ++i) {
- if (!authzSession->isAuthorizedToRevokeRole(roles[i])) {
- return Status(ErrorCodes::Unauthorized,
- str::stream() << "Not authorized to revoke role: " <<
- roles[i].getFullName());
- }
- }
- return Status::OK();
- }
-
- static Status checkAuthorizedToGrantPrivileges(AuthorizationSession* authzSession,
- const PrivilegeVector& privileges) {
- for (PrivilegeVector::const_iterator it = privileges.begin();
- it != privileges.end(); ++it) {
- Status status = authzSession->checkAuthorizedToGrantPrivilege(*it);
- if (!status.isOK()) {
- return status;
- }
- }
-
- return Status::OK();
- }
-
- static Status checkAuthorizedToRevokePrivileges(AuthorizationSession* authzSession,
- const PrivilegeVector& privileges) {
- for (PrivilegeVector::const_iterator it = privileges.begin();
- it != privileges.end(); ++it) {
- Status status = authzSession->checkAuthorizedToRevokePrivilege(*it);
- if (!status.isOK()) {
- return status;
- }
- }
-
- return Status::OK();
- }
-
- /*
- * Checks that every role in "rolesToAdd" exists, that adding each of those roles to "role"
- * will not result in a cycle to the role graph, and that every role being added comes from the
- * same database as the role it is being added to (or that the role being added to is from the
- * "admin" database.
- */
- static Status checkOkayToGrantRolesToRole(const RoleName& role,
- const std::vector<RoleName> rolesToAdd,
- AuthorizationManager* authzManager) {
- for (vector<RoleName>::const_iterator it = rolesToAdd.begin();
- it != rolesToAdd.end(); ++it) {
- const RoleName& roleToAdd = *it;
- if (roleToAdd == role) {
- return Status(ErrorCodes::InvalidRoleModification,
- mongoutils::str::stream() << "Cannot grant role " <<
- role.getFullName() << " to itself.");
- }
-
- if (role.getDB() != "admin" && roleToAdd.getDB() != role.getDB()) {
- return Status(ErrorCodes::InvalidRoleModification,
- str::stream() << "Roles on the \'" << role.getDB() <<
- "\' database cannot be granted roles from other databases");
- }
-
- BSONObj roleToAddDoc;
- Status status = authzManager->getRoleDescription(roleToAdd, false, &roleToAddDoc);
- if (status == ErrorCodes::RoleNotFound) {
- return Status(ErrorCodes::RoleNotFound,
- "Cannot grant nonexistent role " + roleToAdd.toString());
- }
- if (!status.isOK()) {
- return status;
- }
- std::vector<RoleName> indirectRoles;
- status = auth::parseRoleNamesFromBSONArray(
- BSONArray(roleToAddDoc["inheritedRoles"].Obj()),
- role.getDB(),
- &indirectRoles);
- if (!status.isOK()) {
- return status;
- }
-
- if (sequenceContains(indirectRoles, role)) {
- return Status(ErrorCodes::InvalidRoleModification,
- mongoutils::str::stream() << "Granting " <<
- roleToAdd.getFullName() << " to " << role.getFullName()
- << " would introduce a cycle in the role graph.");
- }
- }
- return Status::OK();
- }
-
- /**
- * Checks that every privilege being granted targets just the database the role is from, or that
- * the role is from the "admin" db.
- */
- static Status checkOkayToGrantPrivilegesToRole(const RoleName& role,
- const PrivilegeVector& privileges) {
-
- if (role.getDB() == "admin") {
- return Status::OK();
- }
-
- for (PrivilegeVector::const_iterator it = privileges.begin();
- it != privileges.end(); ++it) {
- const ResourcePattern& resource = (*it).getResourcePattern();
- if ((resource.isDatabasePattern() || resource.isExactNamespacePattern()) &&
- (resource.databaseToMatch() == role.getDB())) {
- continue;
- }
-
- return Status(ErrorCodes::InvalidRoleModification,
- str::stream() << "Roles on the \'" << role.getDB() <<
- "\' database cannot be granted privileges that target other "
- "databases or the cluster");
- }
-
- return Status::OK();
- }
-
- static Status requireAuthSchemaVersion26Final(OperationContext* txn,
- AuthorizationManager* authzManager) {
- int foundSchemaVersion;
- Status status = authzManager->getAuthorizationVersion(txn, &foundSchemaVersion);
- if (!status.isOK()) {
- return status;
- }
-
- if (foundSchemaVersion < AuthorizationManager::schemaVersion26Final) {
- return Status(
- ErrorCodes::AuthSchemaIncompatible,
- str::stream() << "User and role management commands require auth data to have "
- "at least schema version " << AuthorizationManager::schemaVersion26Final <<
- " but found " << foundSchemaVersion);
- }
- return authzManager->writeAuthSchemaVersionIfNeeded(txn, foundSchemaVersion);
- }
-
- static Status requireAuthSchemaVersion26UpgradeOrFinal(OperationContext* txn,
- AuthorizationManager* authzManager) {
- int foundSchemaVersion;
- Status status = authzManager->getAuthorizationVersion(txn, &foundSchemaVersion);
- if (!status.isOK()) {
- return status;
- }
-
- if (foundSchemaVersion < AuthorizationManager::schemaVersion26Upgrade) {
- return Status(
- ErrorCodes::AuthSchemaIncompatible,
- str::stream() << "The usersInfo and rolesInfo commands require auth data to "
- "have at least schema version " <<
- AuthorizationManager::schemaVersion26Upgrade <<
- " but found " << foundSchemaVersion);
- }
- return Status::OK();
- }
-
- static void appendBSONObjToBSONArrayBuilder(BSONArrayBuilder* array, const BSONObj& obj) {
- array->append(obj);
- }
-
class CmdCreateUser : public Command {
public:
@@ -325,7 +83,7 @@ namespace mongo {
return false;
}
- virtual bool isWriteCommandForConfigServer() const { return false; }
+ virtual bool isWriteCommandForConfigServer() const { return true; }
virtual void help(stringstream& ss) const {
ss << "Adds a user to the system" << endl;
@@ -334,28 +92,11 @@ namespace mongo {
virtual Status checkAuthForCommand(ClientBasic* client,
const std::string& dbname,
const BSONObj& cmdObj) {
- AuthorizationSession* authzSession = AuthorizationSession::get(client);
- auth::CreateOrUpdateUserArgs args;
- Status status = auth::parseCreateOrUpdateUserCommands(cmdObj,
- "createUser",
- dbname,
- &args);
- if (!status.isOK()) {
- return status;
- }
-
- if (!authzSession->isAuthorizedForActionsOnResource(
- ResourcePattern::forDatabaseName(args.userName.getDB()),
- ActionType::createUser)) {
- return Status(ErrorCodes::Unauthorized,
- str::stream() << "Not authorized to create users on db: " <<
- args.userName.getDB());
- }
-
- return checkAuthorizedToGrantRoles(authzSession, args.roles);
+ return auth::checkAuthForCreateUserCommand(client, dbname, cmdObj);
}
- bool run(OperationContext* txn, const string& dbname,
+ bool run(OperationContext* txn,
+ const string& dbname,
BSONObj& cmdObj,
int options,
string& errmsg,
@@ -450,7 +191,7 @@ namespace mongo {
if (args.hasCustomData) {
userObjBuilder.append("customData", args.customData);
}
- userObjBuilder.append("roles", rolesVectorToBSONArray(args.roles));
+ userObjBuilder.append("roles", auth::rolesVectorToBSONArray(args.roles));
BSONObj userObj = userObjBuilder.obj();
V2UserDocumentParser parser;
@@ -466,7 +207,7 @@ namespace mongo {
Status(ErrorCodes::LockBusy, "Could not lock auth data update lock."));
}
- status = requireAuthSchemaVersion26Final(txn, authzManager);
+ status = auth::requireAuthSchemaVersion26Final(txn, authzManager);
if (!status.isOK()) {
return appendCommandStatus(result, status);
}
@@ -493,7 +234,7 @@ namespace mongo {
}
virtual void redactForLogging(mutablebson::Document* cmdObj) {
- redactPasswordData(cmdObj->root());
+ auth::redactPasswordData(cmdObj->root());
}
} cmdCreateUser;
@@ -507,7 +248,7 @@ namespace mongo {
return false;
}
- virtual bool isWriteCommandForConfigServer() const { return false; }
+ virtual bool isWriteCommandForConfigServer() const { return true; }
virtual void help(stringstream& ss) const {
ss << "Used to update a user, for example to change its password" << endl;
@@ -516,51 +257,7 @@ namespace mongo {
virtual Status checkAuthForCommand(ClientBasic* client,
const std::string& dbname,
const BSONObj& cmdObj) {
- AuthorizationSession* authzSession = AuthorizationSession::get(client);
- auth::CreateOrUpdateUserArgs args;
- Status status = auth::parseCreateOrUpdateUserCommands(cmdObj,
- "updateUser",
- dbname,
- &args);
- if (!status.isOK()) {
- return status;
- }
-
- if (args.hasHashedPassword) {
- if (!authzSession->isAuthorizedToChangeOwnPasswordAsUser(args.userName) &&
- !authzSession->isAuthorizedForActionsOnResource(
- ResourcePattern::forDatabaseName(args.userName.getDB()),
- ActionType::changePassword)) {
- return Status(ErrorCodes::Unauthorized,
- str::stream() << "Not authorized to change password of user: " <<
- args.userName.getFullName());
- }
- }
-
- if (args.hasCustomData) {
- if (!authzSession->isAuthorizedToChangeOwnCustomDataAsUser(args.userName) &&
- !authzSession->isAuthorizedForActionsOnResource(
- ResourcePattern::forDatabaseName(args.userName.getDB()),
- ActionType::changeCustomData)) {
- return Status(ErrorCodes::Unauthorized,
- str::stream() << "Not authorized to change customData of user: "
- << args.userName.getFullName());
- }
- }
-
- if (args.hasRoles) {
- // You don't know what roles you might be revoking, so require the ability to
- // revoke any role in the system.
- if (!authzSession->isAuthorizedForActionsOnResource(
- ResourcePattern::forAnyNormalResource(), ActionType::revokeRole)) {
- return Status(ErrorCodes::Unauthorized,
- "In order to use updateUser to set roles array, must be "
- "authorized to revoke any role in the system");
- }
-
- return checkAuthorizedToGrantRoles(authzSession, args.roles);
- }
- return Status::OK();
+ return auth::checkAuthForUpdateUserCommand(client, dbname, cmdObj);
}
bool run(OperationContext* txn, const string& dbname,
@@ -619,7 +316,7 @@ namespace mongo {
updateSetBuilder.append("customData", args.customData);
}
if (args.hasRoles) {
- updateSetBuilder.append("roles", rolesVectorToBSONArray(args.roles));
+ updateSetBuilder.append("roles", auth::rolesVectorToBSONArray(args.roles));
}
AuthorizationManager* authzManager = getGlobalAuthorizationManager();
@@ -630,7 +327,7 @@ namespace mongo {
Status(ErrorCodes::LockBusy, "Could not lock auth data update lock."));
}
- status = requireAuthSchemaVersion26Final(txn, authzManager);
+ status = auth::requireAuthSchemaVersion26Final(txn, authzManager);
if (!status.isOK()) {
return appendCommandStatus(result, status);
}
@@ -663,7 +360,7 @@ namespace mongo {
}
virtual void redactForLogging(mutablebson::Document* cmdObj) {
- redactPasswordData(cmdObj->root());
+ auth::redactPasswordData(cmdObj->root());
}
} cmdUpdateUser;
@@ -677,7 +374,7 @@ namespace mongo {
return false;
}
- virtual bool isWriteCommandForConfigServer() const { return false; }
+ virtual bool isWriteCommandForConfigServer() const { return true; }
virtual void help(stringstream& ss) const {
ss << "Drops a single user." << endl;
@@ -686,24 +383,7 @@ namespace mongo {
virtual Status checkAuthForCommand(ClientBasic* client,
const std::string& dbname,
const BSONObj& cmdObj) {
- AuthorizationSession* authzSession = AuthorizationSession::get(client);
- UserName userName;
- BSONObj unusedWriteConcern;
- Status status = auth::parseAndValidateDropUserCommand(cmdObj,
- dbname,
- &userName,
- &unusedWriteConcern);
- if (!status.isOK()) {
- return status;
- }
-
- if (!authzSession->isAuthorizedForActionsOnResource(
- ResourcePattern::forDatabaseName(userName.getDB()), ActionType::dropUser)) {
- return Status(ErrorCodes::Unauthorized,
- str::stream() << "Not authorized to drop users from the " <<
- userName.getDB() << " database");
- }
- return Status::OK();
+ return auth::checkAuthForDropUserCommand(client, dbname, cmdObj);
}
bool run(OperationContext* txn, const string& dbname,
@@ -719,7 +399,7 @@ namespace mongo {
Status(ErrorCodes::LockBusy, "Could not lock auth data update lock."));
}
- Status status = requireAuthSchemaVersion26Final(txn, authzManager);
+ Status status = auth::requireAuthSchemaVersion26Final(txn, authzManager);
if (!status.isOK()) {
return appendCommandStatus(result, status);
}
@@ -773,7 +453,7 @@ namespace mongo {
return false;
}
- virtual bool isWriteCommandForConfigServer() const { return false; }
+ virtual bool isWriteCommandForConfigServer() const { return true; }
virtual void help(stringstream& ss) const {
ss << "Drops all users for a single database." << endl;
@@ -782,14 +462,7 @@ namespace mongo {
virtual Status checkAuthForCommand(ClientBasic* client,
const std::string& dbname,
const BSONObj& cmdObj) {
- AuthorizationSession* authzSession = AuthorizationSession::get(client);
- if (!authzSession->isAuthorizedForActionsOnResource(
- ResourcePattern::forDatabaseName(dbname), ActionType::dropUser)) {
- return Status(ErrorCodes::Unauthorized,
- str::stream() << "Not authorized to drop users from the " <<
- dbname << " database");
- }
- return Status::OK();
+ return auth::checkAuthForDropAllUsersFromDatabaseCommand(client, dbname);
}
bool run(OperationContext* txn, const string& dbname,
@@ -805,7 +478,7 @@ namespace mongo {
Status(ErrorCodes::LockBusy, "Could not lock auth data update lock."));
}
- Status status = requireAuthSchemaVersion26Final(txn, authzManager);
+ Status status = auth::requireAuthSchemaVersion26Final(txn, authzManager);
if (!status.isOK()) {
return appendCommandStatus(result, status);
}
@@ -848,7 +521,7 @@ namespace mongo {
return false;
}
- virtual bool isWriteCommandForConfigServer() const { return false; }
+ virtual bool isWriteCommandForConfigServer() const { return true; }
virtual void help(stringstream& ss) const {
ss << "Grants roles to a user." << endl;
@@ -857,21 +530,7 @@ namespace mongo {
virtual Status checkAuthForCommand(ClientBasic* client,
const std::string& dbname,
const BSONObj& cmdObj) {
- AuthorizationSession* authzSession = AuthorizationSession::get(client);
- std::vector<RoleName> roles;
- std::string unusedUserNameString;
- BSONObj unusedWriteConcern;
- Status status = auth::parseRolePossessionManipulationCommands(cmdObj,
- "grantRolesToUser",
- dbname,
- &unusedUserNameString,
- &roles,
- &unusedWriteConcern);
- if (!status.isOK()) {
- return status;
- }
-
- return checkAuthorizedToGrantRoles(authzSession, roles);
+ return auth::checkAuthForGrantRolesToUserCommand(client, dbname, cmdObj);
}
bool run(OperationContext* txn, const string& dbname,
@@ -887,7 +546,7 @@ namespace mongo {
Status(ErrorCodes::LockBusy, "Could not lock auth data update lock."));
}
- Status status = requireAuthSchemaVersion26Final(txn, authzManager);
+ Status status = auth::requireAuthSchemaVersion26Final(txn, authzManager);
if (!status.isOK()) {
return appendCommandStatus(result, status);
}
@@ -907,7 +566,7 @@ namespace mongo {
UserName userName(userNameString, dbname);
unordered_set<RoleName> userRoles;
- status = getCurrentUserRoles(txn, authzManager, userName, &userRoles);
+ status = auth::getCurrentUserRoles(txn, authzManager, userName, &userRoles);
if (!status.isOK()) {
return appendCommandStatus(result, status);
}
@@ -926,7 +585,7 @@ namespace mongo {
audit::logGrantRolesToUser(ClientBasic::getCurrent(),
userName,
roles);
- BSONArray newRolesBSONArray = roleSetToBSONArray(userRoles);
+ BSONArray newRolesBSONArray = auth::roleSetToBSONArray(userRoles);
status = authzManager->updatePrivilegeDocument(
txn, userName, BSON("$set" << BSON("roles" << newRolesBSONArray)), writeConcern);
// Must invalidate even on bad status - what if the write succeeded but the GLE failed?
@@ -945,7 +604,7 @@ namespace mongo {
return false;
}
- virtual bool isWriteCommandForConfigServer() const { return false; }
+ virtual bool isWriteCommandForConfigServer() const { return true; }
virtual void help(stringstream& ss) const {
ss << "Revokes roles from a user." << endl;
@@ -954,21 +613,7 @@ namespace mongo {
virtual Status checkAuthForCommand(ClientBasic* client,
const std::string& dbname,
const BSONObj& cmdObj) {
- AuthorizationSession* authzSession = AuthorizationSession::get(client);
- std::vector<RoleName> roles;
- std::string unusedUserNameString;
- BSONObj unusedWriteConcern;
- Status status = auth::parseRolePossessionManipulationCommands(cmdObj,
- "revokeRolesFromUser",
- dbname,
- &unusedUserNameString,
- &roles,
- &unusedWriteConcern);
- if (!status.isOK()) {
- return status;
- }
-
- return checkAuthorizedToRevokeRoles(authzSession, roles);
+ return auth::checkAuthForRevokeRolesFromUserCommand(client, dbname, cmdObj);
}
bool run(OperationContext* txn, const string& dbname,
@@ -984,7 +629,7 @@ namespace mongo {
Status(ErrorCodes::LockBusy, "Could not lock auth data update lock."));
}
- Status status = requireAuthSchemaVersion26Final(txn, authzManager);
+ Status status = auth::requireAuthSchemaVersion26Final(txn, authzManager);
if (!status.isOK()) {
return appendCommandStatus(result, status);
}
@@ -1004,7 +649,7 @@ namespace mongo {
UserName userName(userNameString, dbname);
unordered_set<RoleName> userRoles;
- status = getCurrentUserRoles(txn, authzManager, userName, &userRoles);
+ status = auth::getCurrentUserRoles(txn, authzManager, userName, &userRoles);
if (!status.isOK()) {
return appendCommandStatus(result, status);
}
@@ -1023,7 +668,7 @@ namespace mongo {
audit::logRevokeRolesFromUser(ClientBasic::getCurrent(),
userName,
roles);
- BSONArray newRolesBSONArray = roleSetToBSONArray(userRoles);
+ BSONArray newRolesBSONArray = auth::roleSetToBSONArray(userRoles);
status = authzManager->updatePrivilegeDocument(
txn, userName, BSON("$set" << BSON("roles" << newRolesBSONArray)), writeConcern);
// Must invalidate even on bad status - what if the write succeeded but the GLE failed?
@@ -1055,35 +700,7 @@ namespace mongo {
virtual Status checkAuthForCommand(ClientBasic* client,
const std::string& dbname,
const BSONObj& cmdObj) {
- AuthorizationSession* authzSession = AuthorizationSession::get(client);
- auth::UsersInfoArgs args;
- Status status = auth::parseUsersInfoCommand(cmdObj, dbname, &args);
- if (!status.isOK()) {
- return status;
- }
-
- if (args.allForDB) {
- if (!authzSession->isAuthorizedForActionsOnResource(
- ResourcePattern::forDatabaseName(dbname), ActionType::viewUser)) {
- return Status(ErrorCodes::Unauthorized,
- str::stream() << "Not authorized to view users from the " <<
- dbname << " database");
- }
- } else {
- for (size_t i = 0; i < args.userNames.size(); ++i) {
- if (authzSession->lookupUser(args.userNames[i])) {
- continue; // Can always view users you are logged in as
- }
- if (!authzSession->isAuthorizedForActionsOnResource(
- ResourcePattern::forDatabaseName(args.userNames[i].getDB()),
- ActionType::viewUser)) {
- return Status(ErrorCodes::Unauthorized,
- str::stream() << "Not authorized to view users from the " <<
- dbname << " database");
- }
- }
- }
- return Status::OK();
+ return auth::checkAuthForUsersInfoCommand(client, dbname, cmdObj);
}
bool run(OperationContext* txn, const string& dbname,
@@ -1098,8 +715,9 @@ namespace mongo {
return appendCommandStatus(result, status);
}
- status = requireAuthSchemaVersion26UpgradeOrFinal(txn,
- getGlobalAuthorizationManager());
+ status =
+ auth::requireAuthSchemaVersion26UpgradeOrFinal(txn,
+ getGlobalAuthorizationManager());
if (!status.isOK()) {
return appendCommandStatus(result, status);
}
@@ -1161,7 +779,7 @@ namespace mongo {
projection.append("credentials", 0);
}
const stdx::function<void(const BSONObj&)> function = stdx::bind(
- appendBSONObjToBSONArrayBuilder,
+ auth::appendBSONObjToBSONArrayBuilder,
&usersArrayBuilder,
stdx::placeholders::_1);
authzManager->queryAuthzDocument(txn,
@@ -1185,7 +803,7 @@ namespace mongo {
return false;
}
- virtual bool isWriteCommandForConfigServer() const { return false; }
+ virtual bool isWriteCommandForConfigServer() const { return true; }
virtual void help(stringstream& ss) const {
ss << "Adds a role to the system" << endl;
@@ -1194,30 +812,7 @@ namespace mongo {
virtual Status checkAuthForCommand(ClientBasic* client,
const std::string& dbname,
const BSONObj& cmdObj) {
- AuthorizationSession* authzSession = AuthorizationSession::get(client);
- auth::CreateOrUpdateRoleArgs args;
- Status status = auth::parseCreateOrUpdateRoleCommands(cmdObj,
- "createRole",
- dbname,
- &args);
- if (!status.isOK()) {
- return status;
- }
-
- if (!authzSession->isAuthorizedForActionsOnResource(
- ResourcePattern::forDatabaseName(args.roleName.getDB()),
- ActionType::createRole)) {
- return Status(ErrorCodes::Unauthorized,
- str::stream() << "Not authorized to create roles on db: " <<
- args.roleName.getDB());
- }
-
- status = checkAuthorizedToGrantRoles(authzSession, args.roles);
- if (!status.isOK()) {
- return status;
- }
-
- return checkAuthorizedToGrantPrivileges(authzSession, args.privileges);
+ return auth::checkAuthForCreateRoleCommand(client, dbname, cmdObj);
}
bool run(OperationContext* txn, const string& dbname,
@@ -1277,13 +872,13 @@ namespace mongo {
args.roleName.getDB());
BSONArray privileges;
- status = privilegeVectorToBSONArray(args.privileges, &privileges);
+ status = auth::privilegeVectorToBSONArray(args.privileges, &privileges);
if (!status.isOK()) {
return appendCommandStatus(result, status);
}
roleObjBuilder.append("privileges", privileges);
- roleObjBuilder.append("roles", rolesVectorToBSONArray(args.roles));
+ roleObjBuilder.append("roles", auth::rolesVectorToBSONArray(args.roles));
AuthorizationManager* authzManager = getGlobalAuthorizationManager();
AuthzDocumentsUpdateGuard updateGuard(authzManager);
@@ -1293,18 +888,18 @@ namespace mongo {
Status(ErrorCodes::LockBusy, "Could not lock auth data update lock."));
}
- status = requireAuthSchemaVersion26Final(txn, authzManager);
+ status = auth::requireAuthSchemaVersion26Final(txn, authzManager);
if (!status.isOK()) {
return appendCommandStatus(result, status);
}
// Role existence has to be checked after acquiring the update lock
- status = checkOkayToGrantRolesToRole(args.roleName, args.roles, authzManager);
+ status = auth::checkOkayToGrantRolesToRole(args.roleName, args.roles, authzManager);
if (!status.isOK()) {
return appendCommandStatus(result, status);
}
- status = checkOkayToGrantPrivilegesToRole(args.roleName, args.privileges);
+ status = auth::checkOkayToGrantPrivilegesToRole(args.roleName, args.privileges);
if (!status.isOK()) {
return appendCommandStatus(result, status);
}
@@ -1329,7 +924,7 @@ namespace mongo {
return false;
}
- virtual bool isWriteCommandForConfigServer() const { return false; }
+ virtual bool isWriteCommandForConfigServer() const { return true; }
virtual void help(stringstream& ss) const {
ss << "Used to update a role" << endl;
@@ -1338,31 +933,7 @@ namespace mongo {
virtual Status checkAuthForCommand(ClientBasic* client,
const std::string& dbname,
const BSONObj& cmdObj) {
- AuthorizationSession* authzSession = AuthorizationSession::get(client);
- auth::CreateOrUpdateRoleArgs args;
- Status status = auth::parseCreateOrUpdateRoleCommands(cmdObj,
- "updateRole",
- dbname,
- &args);
- if (!status.isOK()) {
- return status;
- }
-
- // You don't know what roles or privileges you might be revoking, so require the ability
- // to revoke any role (or privilege) in the system.
- if (!authzSession->isAuthorizedForActionsOnResource(
- ResourcePattern::forAnyNormalResource(), ActionType::revokeRole)) {
- return Status(ErrorCodes::Unauthorized,
- "updateRole command required the ability to revoke any role in the "
- "system");
- }
-
- status = checkAuthorizedToGrantRoles(authzSession, args.roles);
- if (!status.isOK()) {
- return status;
- }
-
- return checkAuthorizedToGrantPrivileges(authzSession, args.privileges);
+ return auth::checkAuthForUpdateRoleCommand(client, dbname, cmdObj);
}
bool run(OperationContext* txn, const string& dbname,
@@ -1390,7 +961,7 @@ namespace mongo {
if (args.hasPrivileges) {
BSONArray privileges;
- status = privilegeVectorToBSONArray(args.privileges, &privileges);
+ status = auth::privilegeVectorToBSONArray(args.privileges, &privileges);
if (!status.isOK()) {
return appendCommandStatus(result, status);
}
@@ -1398,7 +969,7 @@ namespace mongo {
}
if (args.hasRoles) {
- updateSetBuilder.append("roles", rolesVectorToBSONArray(args.roles));
+ updateSetBuilder.append("roles", auth::rolesVectorToBSONArray(args.roles));
}
AuthorizationManager* authzManager = getGlobalAuthorizationManager();
@@ -1409,7 +980,7 @@ namespace mongo {
Status(ErrorCodes::LockBusy, "Could not lock auth data update lock."));
}
- status = requireAuthSchemaVersion26Final(txn, authzManager);
+ status = auth::requireAuthSchemaVersion26Final(txn, authzManager);
if (!status.isOK()) {
return appendCommandStatus(result, status);
}
@@ -1422,14 +993,14 @@ namespace mongo {
}
if (args.hasRoles) {
- status = checkOkayToGrantRolesToRole(args.roleName, args.roles, authzManager);
+ status = auth::checkOkayToGrantRolesToRole(args.roleName, args.roles, authzManager);
if (!status.isOK()) {
return appendCommandStatus(result, status);
}
}
if (args.hasPrivileges) {
- status = checkOkayToGrantPrivilegesToRole(args.roleName, args.privileges);
+ status = auth::checkOkayToGrantPrivilegesToRole(args.roleName, args.privileges);
if (!status.isOK()) {
return appendCommandStatus(result, status);
}
@@ -1459,7 +1030,7 @@ namespace mongo {
return false;
}
- virtual bool isWriteCommandForConfigServer() const { return false; }
+ virtual bool isWriteCommandForConfigServer() const { return true; }
virtual void help(stringstream& ss) const {
ss << "Grants privileges to a role" << endl;
@@ -1468,22 +1039,7 @@ namespace mongo {
virtual Status checkAuthForCommand(ClientBasic* client,
const std::string& dbname,
const BSONObj& cmdObj) {
- AuthorizationSession* authzSession = AuthorizationSession::get(client);
- PrivilegeVector privileges;
- RoleName unusedRoleName;
- BSONObj unusedWriteConcern;
- Status status = auth::parseAndValidateRolePrivilegeManipulationCommands(
- cmdObj,
- "grantPrivilegesToRole",
- dbname,
- &unusedRoleName,
- &privileges,
- &unusedWriteConcern);
- if (!status.isOK()) {
- return status;
- }
-
- return checkAuthorizedToGrantPrivileges(authzSession, privileges);
+ return auth::checkAuthForGrantPrivilegesToRoleCommand(client, dbname, cmdObj);
}
bool run(OperationContext* txn, const string& dbname,
@@ -1499,7 +1055,7 @@ namespace mongo {
Status(ErrorCodes::LockBusy, "Could not lock auth data update lock."));
}
- Status status = requireAuthSchemaVersion26Final(txn, authzManager);
+ Status status = auth::requireAuthSchemaVersion26Final(txn, authzManager);
if (!status.isOK()) {
return appendCommandStatus(result, status);
}
@@ -1526,7 +1082,7 @@ namespace mongo {
" is a built-in role and cannot be modified."));
}
- status = checkOkayToGrantPrivilegesToRole(roleName, privilegesToAdd);
+ status = auth::checkOkayToGrantPrivilegesToRole(roleName, privilegesToAdd);
if (!status.isOK()) {
return appendCommandStatus(result, status);
}
@@ -1595,7 +1151,7 @@ namespace mongo {
return false;
}
- virtual bool isWriteCommandForConfigServer() const { return false; }
+ virtual bool isWriteCommandForConfigServer() const { return true; }
virtual void help(stringstream& ss) const {
ss << "Revokes privileges from a role" << endl;
@@ -1604,22 +1160,7 @@ namespace mongo {
virtual Status checkAuthForCommand(ClientBasic* client,
const std::string& dbname,
const BSONObj& cmdObj) {
- AuthorizationSession* authzSession = AuthorizationSession::get(client);
- PrivilegeVector privileges;
- RoleName unusedRoleName;
- BSONObj unusedWriteConcern;
- Status status = auth::parseAndValidateRolePrivilegeManipulationCommands(
- cmdObj,
- "revokePrivilegesFromRole",
- dbname,
- &unusedRoleName,
- &privileges,
- &unusedWriteConcern);
- if (!status.isOK()) {
- return status;
- }
-
- return checkAuthorizedToRevokePrivileges(authzSession, privileges);
+ return auth::checkAuthForRevokePrivilegesFromRoleCommand(client, dbname, cmdObj);
}
bool run(OperationContext* txn, const string& dbname,
@@ -1635,7 +1176,7 @@ namespace mongo {
Status(ErrorCodes::LockBusy, "Could not lock auth data update lock."));
}
- Status status = requireAuthSchemaVersion26Final(txn, authzManager);
+ Status status = auth::requireAuthSchemaVersion26Final(txn, authzManager);
if (!status.isOK()) {
return appendCommandStatus(result, status);
}
@@ -1733,7 +1274,7 @@ namespace mongo {
return false;
}
- virtual bool isWriteCommandForConfigServer() const { return false; }
+ virtual bool isWriteCommandForConfigServer() const { return true; }
virtual void help(stringstream& ss) const {
ss << "Grants roles to another role." << endl;
@@ -1742,21 +1283,7 @@ namespace mongo {
virtual Status checkAuthForCommand(ClientBasic* client,
const std::string& dbname,
const BSONObj& cmdObj) {
- AuthorizationSession* authzSession = AuthorizationSession::get(client);
- std::vector<RoleName> roles;
- std::string unusedUserNameString;
- BSONObj unusedWriteConcern;
- Status status = auth::parseRolePossessionManipulationCommands(cmdObj,
- "grantRolesToRole",
- dbname,
- &unusedUserNameString,
- &roles,
- &unusedWriteConcern);
- if (!status.isOK()) {
- return status;
- }
-
- return checkAuthorizedToGrantRoles(authzSession, roles);
+ return auth::checkAuthForGrantRolesToRoleCommand(client, dbname, cmdObj);
}
bool run(OperationContext* txn, const string& dbname,
@@ -1795,7 +1322,7 @@ namespace mongo {
Status(ErrorCodes::LockBusy, "Could not lock auth data update lock."));
}
- status = requireAuthSchemaVersion26Final(txn, authzManager);
+ status = auth::requireAuthSchemaVersion26Final(txn, authzManager);
if (!status.isOK()) {
return appendCommandStatus(result, status);
}
@@ -1808,7 +1335,7 @@ namespace mongo {
}
// Check for cycles
- status = checkOkayToGrantRolesToRole(roleName, rolesToAdd, authzManager);
+ status = auth::checkOkayToGrantRolesToRole(roleName, rolesToAdd, authzManager);
if (!status.isOK()) {
return appendCommandStatus(result, status);
}
@@ -1834,7 +1361,7 @@ namespace mongo {
status = authzManager->updateRoleDocument(
txn,
roleName,
- BSON("$set" << BSON("roles" << rolesVectorToBSONArray(directRoles))),
+ BSON("$set" << BSON("roles" << auth::rolesVectorToBSONArray(directRoles))),
writeConcern);
// Must invalidate even on bad status - what if the write succeeded but the GLE failed?
authzManager->invalidateUserCache();
@@ -1852,7 +1379,7 @@ namespace mongo {
return false;
}
- virtual bool isWriteCommandForConfigServer() const { return false; }
+ virtual bool isWriteCommandForConfigServer() const { return true; }
virtual void help(stringstream& ss) const {
ss << "Revokes roles from another role." << endl;
@@ -1861,21 +1388,7 @@ namespace mongo {
virtual Status checkAuthForCommand(ClientBasic* client,
const std::string& dbname,
const BSONObj& cmdObj) {
- AuthorizationSession* authzSession = AuthorizationSession::get(client);
- std::vector<RoleName> roles;
- std::string unusedUserNameString;
- BSONObj unusedWriteConcern;
- Status status = auth::parseRolePossessionManipulationCommands(cmdObj,
- "revokeRolesFromRole",
- dbname,
- &unusedUserNameString,
- &roles,
- &unusedWriteConcern);
- if (!status.isOK()) {
- return status;
- }
-
- return checkAuthorizedToRevokeRoles(authzSession, roles);
+ return auth::checkAuthForRevokeRolesFromRoleCommand(client, dbname, cmdObj);
}
bool run(OperationContext* txn, const string& dbname,
@@ -1891,7 +1404,7 @@ namespace mongo {
Status(ErrorCodes::LockBusy, "Could not lock auth data update lock."));
}
- Status status = requireAuthSchemaVersion26Final(txn, authzManager);
+ Status status = auth::requireAuthSchemaVersion26Final(txn, authzManager);
if (!status.isOK()) {
return appendCommandStatus(result, status);
}
@@ -1947,7 +1460,7 @@ namespace mongo {
status = authzManager->updateRoleDocument(
txn,
roleName,
- BSON("$set" << BSON("roles" << rolesVectorToBSONArray(roles))),
+ BSON("$set" << BSON("roles" << auth::rolesVectorToBSONArray(roles))),
writeConcern);
// Must invalidate even on bad status - what if the write succeeded but the GLE failed?
authzManager->invalidateUserCache();
@@ -1965,7 +1478,7 @@ namespace mongo {
return false;
}
- virtual bool isWriteCommandForConfigServer() const { return false; }
+ virtual bool isWriteCommandForConfigServer() const { return true; }
virtual void help(stringstream& ss) const {
ss << "Drops a single role. Before deleting the role completely it must remove it "
@@ -1977,24 +1490,7 @@ namespace mongo {
virtual Status checkAuthForCommand(ClientBasic* client,
const std::string& dbname,
const BSONObj& cmdObj) {
- AuthorizationSession* authzSession = AuthorizationSession::get(client);
- RoleName roleName;
- BSONObj unusedWriteConcern;
- Status status = auth::parseDropRoleCommand(cmdObj,
- dbname,
- &roleName,
- &unusedWriteConcern);
- if (!status.isOK()) {
- return status;
- }
-
- if (!authzSession->isAuthorizedForActionsOnResource(
- ResourcePattern::forDatabaseName(roleName.getDB()), ActionType::dropRole)) {
- return Status(ErrorCodes::Unauthorized,
- str::stream() << "Not authorized to drop roles from the " <<
- roleName.getDB() << " database");
- }
- return Status::OK();
+ return auth::checkAuthForDropRoleCommand(client, dbname, cmdObj);
}
bool run(OperationContext* txn, const string& dbname,
@@ -2010,7 +1506,7 @@ namespace mongo {
Status(ErrorCodes::LockBusy, "Could not lock auth data update lock."));
}
- Status status = requireAuthSchemaVersion26Final(txn, authzManager);
+ Status status = auth::requireAuthSchemaVersion26Final(txn, authzManager);
if (!status.isOK()) {
return appendCommandStatus(result, status);
}
@@ -2144,7 +1640,7 @@ namespace mongo {
return false;
}
- virtual bool isWriteCommandForConfigServer() const { return false; }
+ virtual bool isWriteCommandForConfigServer() const { return true; }
virtual void help(stringstream& ss) const {
ss << "Drops all roles from the given database. Before deleting the roles completely "
@@ -2157,14 +1653,7 @@ namespace mongo {
virtual Status checkAuthForCommand(ClientBasic* client,
const std::string& dbname,
const BSONObj& cmdObj) {
- AuthorizationSession* authzSession = AuthorizationSession::get(client);
- if (!authzSession->isAuthorizedForActionsOnResource(
- ResourcePattern::forDatabaseName(dbname), ActionType::dropRole)) {
- return Status(ErrorCodes::Unauthorized,
- str::stream() << "Not authorized to drop roles from the " <<
- dbname << " database");
- }
- return Status::OK();
+ return auth::checkAuthForDropAllRolesFromDatabaseCommand(client, dbname);
}
bool run(OperationContext* txn, const string& dbname,
@@ -2188,7 +1677,7 @@ namespace mongo {
Status(ErrorCodes::LockBusy, "Could not lock auth data update lock."));
}
- status = requireAuthSchemaVersion26Final(txn, authzManager);
+ status = auth::requireAuthSchemaVersion26Final(txn, authzManager);
if (!status.isOK()) {
return appendCommandStatus(result, status);
}
@@ -2291,37 +1780,7 @@ namespace mongo {
virtual Status checkAuthForCommand(ClientBasic* client,
const std::string& dbname,
const BSONObj& cmdObj) {
- AuthorizationSession* authzSession = AuthorizationSession::get(client);
- auth::RolesInfoArgs args;
- Status status = auth::parseRolesInfoCommand(cmdObj, dbname, &args);
- if (!status.isOK()) {
- return status;
- }
-
- if (args.allForDB) {
- if (!authzSession->isAuthorizedForActionsOnResource(
- ResourcePattern::forDatabaseName(dbname), ActionType::viewRole)) {
- return Status(ErrorCodes::Unauthorized,
- str::stream() << "Not authorized to view roles from the " <<
- dbname << " database");
- }
- } else {
- for (size_t i = 0; i < args.roleNames.size(); ++i) {
- if (authzSession->isAuthenticatedAsUserWithRole(args.roleNames[i])) {
- continue; // Can always see roles that you are a member of
- }
-
- if (!authzSession->isAuthorizedForActionsOnResource(
- ResourcePattern::forDatabaseName(args.roleNames[i].getDB()),
- ActionType::viewRole)) {
- return Status(ErrorCodes::Unauthorized,
- str::stream() << "Not authorized to view roles from the " <<
- args.roleNames[i].getDB() << " database");
- }
- }
- }
-
- return Status::OK();
+ return auth::checkAuthForRolesInfoCommand(client, dbname, cmdObj);
}
bool run(OperationContext* txn, const string& dbname,
@@ -2336,8 +1795,9 @@ namespace mongo {
return appendCommandStatus(result, status);
}
- status = requireAuthSchemaVersion26UpgradeOrFinal(txn,
- getGlobalAuthorizationManager());
+ status =
+ auth::requireAuthSchemaVersion26UpgradeOrFinal(txn,
+ getGlobalAuthorizationManager());
if (!status.isOK()) {
return appendCommandStatus(result, status);
}
@@ -2396,12 +1856,7 @@ namespace mongo {
virtual Status checkAuthForCommand(ClientBasic* client,
const std::string& dbname,
const BSONObj& cmdObj) {
- AuthorizationSession* authzSession = AuthorizationSession::get(client);
- if (!authzSession->isAuthorizedForActionsOnResource(
- ResourcePattern::forClusterResource(), ActionType::invalidateUserCache)) {
- return Status(ErrorCodes::Unauthorized, "Not authorized to invalidate user cache");
- }
- return Status::OK();
+ return auth::checkAuthForInvalidateUserCacheCommand(client);
}
bool run(OperationContext* txn, const string& dbname,
@@ -2439,12 +1894,7 @@ namespace mongo {
virtual Status checkAuthForCommand(ClientBasic* client,
const std::string& dbname,
const BSONObj& cmdObj) {
- AuthorizationSession* authzSession = AuthorizationSession::get(client);
- if (!authzSession->isAuthorizedForActionsOnResource(
- ResourcePattern::forClusterResource(), ActionType::internal)) {
- return Status(ErrorCodes::Unauthorized, "Not authorized to get cache generation");
- }
- return Status::OK();
+ return auth::checkAuthForGetUserCacheGenerationCommand(client);
}
bool run(OperationContext* txn,
@@ -2480,7 +1930,7 @@ namespace mongo {
return false;
}
- virtual bool isWriteCommandForConfigServer() const { return false; }
+ virtual bool isWriteCommandForConfigServer() const { return true; }
virtual bool adminOnly() const {
return true;
@@ -2493,45 +1943,7 @@ namespace mongo {
virtual Status checkAuthForCommand(ClientBasic* client,
const std::string& dbname,
const BSONObj& cmdObj) {
- auth::MergeAuthzCollectionsArgs args;
- Status status = auth::parseMergeAuthzCollectionsCommand(cmdObj, &args);
- if (!status.isOK()) {
- return status;
- }
-
- AuthorizationSession* authzSession = AuthorizationSession::get(client);
- ActionSet actions;
- actions.addAction(ActionType::createUser);
- actions.addAction(ActionType::createRole);
- actions.addAction(ActionType::grantRole);
- actions.addAction(ActionType::revokeRole);
- if (args.drop) {
- actions.addAction(ActionType::dropUser);
- actions.addAction(ActionType::dropRole);
- }
- if (!authzSession->isAuthorizedForActionsOnResource(
- ResourcePattern::forAnyNormalResource(), actions)) {
- return Status(ErrorCodes::Unauthorized,
- "Not authorized to update user/role data using _mergeAuthzCollections"
- " command");
- }
- if (!args.usersCollName.empty() &&
- !authzSession->isAuthorizedForActionsOnResource(
- ResourcePattern::forExactNamespace(NamespaceString(args.usersCollName)),
- ActionType::find)) {
- return Status(ErrorCodes::Unauthorized,
- mongoutils::str::stream() << "Not authorized to read " <<
- args.usersCollName);
- }
- if (!args.rolesCollName.empty() &&
- !authzSession->isAuthorizedForActionsOnResource(
- ResourcePattern::forExactNamespace(NamespaceString(args.rolesCollName)),
- ActionType::find)) {
- return Status(ErrorCodes::Unauthorized,
- mongoutils::str::stream() << "Not authorized to read " <<
- args.rolesCollName);
- }
- return Status::OK();
+ return auth::checkAuthForMergeAuthzCollectionsCommand(client, cmdObj);
}
static UserName extractUserNameFromBSON(const BSONObj& userObj) {
@@ -2916,7 +2328,7 @@ namespace mongo {
Status(ErrorCodes::LockBusy, "Could not lock auth data update lock."));
}
- status = requireAuthSchemaVersion26Final(txn, authzManager);
+ status = auth::requireAuthSchemaVersion26Final(txn, authzManager);
if (!status.isOK()) {
return appendCommandStatus(result, status);
}
diff --git a/src/mongo/db/commands/user_management_commands.h b/src/mongo/db/commands/user_management_commands.h
new file mode 100644
index 00000000000..5f21c719117
--- /dev/null
+++ b/src/mongo/db/commands/user_management_commands.h
@@ -0,0 +1,199 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "mongo/bson/mutable/element.h"
+#include "mongo/db/auth/privilege.h"
+#include "mongo/db/auth/role_name.h"
+#include "mongo/db/auth/user_name.h"
+#include "mongo/platform/unordered_set.h"
+
+namespace mongo {
+
+ class AuthorizationManager;
+ class AuthorizationSession;
+ struct BSONArray;
+ class BSONObj;
+ class ClientBasic;
+ class OperationContext;
+
+namespace auth {
+
+ /**
+ * Looks for a field name "pwd" in the given BSONObj and if found replaces its contents with the
+ * string "xxx" so that password data on the command object used in executing a user management
+ * command isn't exposed in the logs.
+ */
+ void redactPasswordData(mutablebson::Element parent);
+
+ BSONArray roleSetToBSONArray(const unordered_set<RoleName>& roles);
+
+ BSONArray rolesVectorToBSONArray(const std::vector<RoleName>& roles);
+
+ Status privilegeVectorToBSONArray(const PrivilegeVector& privileges, BSONArray* result);
+
+ /**
+ * Used to get all current roles of the user identified by 'userName'.
+ */
+ Status getCurrentUserRoles(OperationContext* txn,
+ AuthorizationManager* authzManager,
+ const UserName& userName,
+ unordered_set<RoleName>* roles);
+
+ /**
+ * Checks that every role in "rolesToAdd" exists, that adding each of those roles to "role"
+ * will not result in a cycle to the role graph, and that every role being added comes from the
+ * same database as the role it is being added to (or that the role being added to is from the
+ * "admin" database.
+ */
+ Status checkOkayToGrantRolesToRole(const RoleName& role,
+ const std::vector<RoleName> rolesToAdd,
+ AuthorizationManager* authzManager);
+
+ /**
+ * Checks that every privilege being granted targets just the database the role is from, or that
+ * the role is from the "admin" db.
+ */
+ Status checkOkayToGrantPrivilegesToRole(const RoleName& role,
+ const PrivilegeVector& privileges);
+
+ /**
+ * Returns Status::OK() if the current Auth schema version is at least the auth schema version
+ * for the MongoDB 2.6 and 3.0 MongoDB-CR/SCRAM mixed auth mode.
+ * Returns an error otherwise.
+ */
+ Status requireAuthSchemaVersion26Final(OperationContext* txn,
+ AuthorizationManager* authzManager);
+
+ /**
+ * Returns Status::OK() if the current Auth schema version is at least the auth schema version
+ * for MongoDB 2.6 during the upgrade process.
+ * Returns an error otherwise.
+ */
+ Status requireAuthSchemaVersion26UpgradeOrFinal(OperationContext* txn,
+ AuthorizationManager* authzManager);
+
+ void appendBSONObjToBSONArrayBuilder(BSONArrayBuilder* array, const BSONObj& obj);
+
+ //
+ // checkAuthorizedTo* methods
+ //
+
+ Status checkAuthorizedToGrantRoles(AuthorizationSession* authzSession,
+ const std::vector<RoleName>& roles);
+
+ Status checkAuthorizedToGrantPrivileges(AuthorizationSession* authzSession,
+ const PrivilegeVector& privileges);
+
+ Status checkAuthorizedToRevokeRoles(AuthorizationSession* authzSession,
+ const std::vector<RoleName>& roles);
+
+ Status checkAuthorizedToRevokePrivileges(AuthorizationSession* authzSession,
+ const PrivilegeVector& privileges);
+
+ //
+ // checkAuthFor*Command methods
+ //
+
+ Status checkAuthForCreateUserCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj);
+
+ Status checkAuthForUpdateUserCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj);
+
+ Status checkAuthForGrantRolesToUserCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj);
+
+ Status checkAuthForCreateRoleCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj);
+
+ Status checkAuthForUpdateRoleCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj);
+
+ Status checkAuthForGrantRolesToRoleCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj);
+
+ Status checkAuthForGrantPrivilegesToRoleCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj);
+
+ Status checkAuthForDropAllUsersFromDatabaseCommand(ClientBasic* client,
+ const std::string& dbname);
+
+ Status checkAuthForRevokeRolesFromUserCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj);
+
+ Status checkAuthForRevokeRolesFromRoleCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj);
+
+ Status checkAuthForDropUserCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj);
+
+ Status checkAuthForDropRoleCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj);
+
+
+ Status checkAuthForUsersInfoCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj);
+
+ Status checkAuthForRevokePrivilegesFromRoleCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj);
+
+ Status checkAuthForDropAllRolesFromDatabaseCommand(ClientBasic* client,
+ const std::string& dbname);
+
+ Status checkAuthForRolesInfoCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj);
+
+ Status checkAuthForInvalidateUserCacheCommand(ClientBasic* client);
+
+ Status checkAuthForGetUserCacheGenerationCommand(ClientBasic* client);
+
+ Status checkAuthForMergeAuthzCollectionsCommand(ClientBasic* client,
+ const BSONObj& cmdObj);
+
+
+} // namespace auth
+} // namespace mongo
diff --git a/src/mongo/db/commands/user_management_commands_common.cpp b/src/mongo/db/commands/user_management_commands_common.cpp
new file mode 100644
index 00000000000..cfe634f142a
--- /dev/null
+++ b/src/mongo/db/commands/user_management_commands_common.cpp
@@ -0,0 +1,749 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kAccessControl
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/commands/user_management_commands.h"
+
+#include <string>
+#include <vector>
+
+#include "mongo/base/status.h"
+#include "mongo/bson/mutable/algorithm.h"
+#include "mongo/config.h"
+#include "mongo/db/auth/action_set.h"
+#include "mongo/db/auth/action_type.h"
+#include "mongo/db/auth/authorization_manager.h"
+#include "mongo/db/auth/authorization_session.h"
+#include "mongo/db/auth/resource_pattern.h"
+#include "mongo/db/auth/user.h"
+#include "mongo/db/auth/user_management_commands_parser.h"
+#include "mongo/db/jsobj.h"
+#include "mongo/util/log.h"
+#include "mongo/util/mongoutils/str.h"
+#include "mongo/util/sequence_util.h"
+
+namespace mongo {
+namespace auth {
+
+ void redactPasswordData(mutablebson::Element parent) {
+ namespace mmb = mutablebson;
+ const StringData pwdFieldName("pwd", StringData::LiteralTag());
+ for (mmb::Element pwdElement = mmb::findFirstChildNamed(parent, pwdFieldName);
+ pwdElement.ok();
+ pwdElement = mmb::findElementNamed(pwdElement.rightSibling(), pwdFieldName)) {
+
+ pwdElement.setValueString("xxx");
+ }
+ }
+
+ BSONArray roleSetToBSONArray(const unordered_set<RoleName>& roles) {
+ BSONArrayBuilder rolesArrayBuilder;
+ for (unordered_set<RoleName>::const_iterator it = roles.begin(); it != roles.end(); ++it) {
+ const RoleName& role = *it;
+ rolesArrayBuilder.append(
+ BSON(AuthorizationManager::ROLE_NAME_FIELD_NAME << role.getRole() <<
+ AuthorizationManager::ROLE_DB_FIELD_NAME << role.getDB()));
+ }
+ return rolesArrayBuilder.arr();
+ }
+
+ BSONArray rolesVectorToBSONArray(const std::vector<RoleName>& roles) {
+ BSONArrayBuilder rolesArrayBuilder;
+ for (std::vector<RoleName>::const_iterator it = roles.begin(); it != roles.end(); ++it) {
+ const RoleName& role = *it;
+ rolesArrayBuilder.append(
+ BSON(AuthorizationManager::ROLE_NAME_FIELD_NAME << role.getRole() <<
+ AuthorizationManager::ROLE_DB_FIELD_NAME << role.getDB()));
+ }
+ return rolesArrayBuilder.arr();
+ }
+
+ 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();
+ }
+
+ Status getCurrentUserRoles(OperationContext* txn,
+ AuthorizationManager* authzManager,
+ const UserName& userName,
+ unordered_set<RoleName>* roles) {
+ User* user;
+ authzManager->invalidateUserByName(userName); // Need to make sure cache entry is up to date
+ Status status = authzManager->acquireUser(txn, userName, &user);
+ if (!status.isOK()) {
+ return status;
+ }
+ RoleNameIterator rolesIt = user->getRoles();
+ while (rolesIt.more()) {
+ roles->insert(rolesIt.next());
+ }
+ authzManager->releaseUser(user);
+ return Status::OK();
+ }
+
+ Status checkOkayToGrantRolesToRole(const RoleName& role,
+ const std::vector<RoleName> rolesToAdd,
+ AuthorizationManager* authzManager) {
+ for (std::vector<RoleName>::const_iterator it = rolesToAdd.begin();
+ it != rolesToAdd.end(); ++it) {
+ const RoleName& roleToAdd = *it;
+ if (roleToAdd == role) {
+ return Status(ErrorCodes::InvalidRoleModification,
+ mongoutils::str::stream() << "Cannot grant role " <<
+ role.getFullName() << " to itself.");
+ }
+
+ if (role.getDB() != "admin" && roleToAdd.getDB() != role.getDB()) {
+ return Status(ErrorCodes::InvalidRoleModification,
+ str::stream() << "Roles on the \'" << role.getDB() <<
+ "\' database cannot be granted roles from other databases");
+ }
+
+ BSONObj roleToAddDoc;
+ Status status = authzManager->getRoleDescription(roleToAdd, false, &roleToAddDoc);
+ if (status == ErrorCodes::RoleNotFound) {
+ return Status(ErrorCodes::RoleNotFound,
+ "Cannot grant nonexistent role " + roleToAdd.toString());
+ }
+ if (!status.isOK()) {
+ return status;
+ }
+ std::vector<RoleName> indirectRoles;
+ status = auth::parseRoleNamesFromBSONArray(
+ BSONArray(roleToAddDoc["inheritedRoles"].Obj()),
+ role.getDB(),
+ &indirectRoles);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ if (sequenceContains(indirectRoles, role)) {
+ return Status(ErrorCodes::InvalidRoleModification,
+ mongoutils::str::stream() << "Granting " <<
+ roleToAdd.getFullName() << " to " << role.getFullName()
+ << " would introduce a cycle in the role graph.");
+ }
+ }
+ return Status::OK();
+ }
+
+ Status checkOkayToGrantPrivilegesToRole(const RoleName& role,
+ const PrivilegeVector& privileges) {
+ if (role.getDB() == "admin") {
+ return Status::OK();
+ }
+
+ for (PrivilegeVector::const_iterator it = privileges.begin();
+ it != privileges.end(); ++it) {
+ const ResourcePattern& resource = (*it).getResourcePattern();
+ if ((resource.isDatabasePattern() || resource.isExactNamespacePattern()) &&
+ (resource.databaseToMatch() == role.getDB())) {
+ continue;
+ }
+
+ return Status(ErrorCodes::InvalidRoleModification,
+ str::stream() << "Roles on the \'" << role.getDB() <<
+ "\' database cannot be granted privileges that target other "
+ "databases or the cluster");
+ }
+
+ return Status::OK();
+ }
+
+ Status requireAuthSchemaVersion26Final(OperationContext* txn,
+ AuthorizationManager* authzManager) {
+ int foundSchemaVersion;
+ Status status = authzManager->getAuthorizationVersion(txn, &foundSchemaVersion);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ if (foundSchemaVersion < AuthorizationManager::schemaVersion26Final) {
+ return Status(
+ ErrorCodes::AuthSchemaIncompatible,
+ str::stream() << "User and role management commands require auth data to have "
+ "at least schema version " << AuthorizationManager::schemaVersion26Final <<
+ " but found " << foundSchemaVersion);
+ }
+ return authzManager->writeAuthSchemaVersionIfNeeded(txn, foundSchemaVersion);
+ }
+
+ Status requireAuthSchemaVersion26UpgradeOrFinal(OperationContext* txn,
+ AuthorizationManager* authzManager) {
+ int foundSchemaVersion;
+ Status status = authzManager->getAuthorizationVersion(txn, &foundSchemaVersion);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ if (foundSchemaVersion < AuthorizationManager::schemaVersion26Upgrade) {
+ return Status(
+ ErrorCodes::AuthSchemaIncompatible,
+ str::stream() << "The usersInfo and rolesInfo commands require auth data to "
+ "have at least schema version " <<
+ AuthorizationManager::schemaVersion26Upgrade <<
+ " but found " << foundSchemaVersion);
+ }
+ return Status::OK();
+ }
+
+ void appendBSONObjToBSONArrayBuilder(BSONArrayBuilder* array, const BSONObj& obj) {
+ array->append(obj);
+ }
+
+ Status checkAuthorizedToGrantRoles(AuthorizationSession* authzSession,
+ const std::vector<RoleName>& roles) {
+ for (size_t i = 0; i < roles.size(); ++i) {
+ if (!authzSession->isAuthorizedToGrantRole(roles[i])) {
+ return Status(ErrorCodes::Unauthorized,
+ str::stream() << "Not authorized to grant role: "
+ << roles[i].getFullName());
+ }
+ }
+
+ return Status::OK();
+ }
+
+ Status checkAuthorizedToGrantPrivileges(AuthorizationSession* authzSession,
+ const PrivilegeVector& privileges) {
+ for (PrivilegeVector::const_iterator it = privileges.begin();
+ it != privileges.end(); ++it) {
+ Status status = authzSession->checkAuthorizedToGrantPrivilege(*it);
+ if (!status.isOK()) {
+ return status;
+ }
+ }
+
+ return Status::OK();
+ }
+
+ Status checkAuthorizedToRevokeRoles(AuthorizationSession* authzSession,
+ const std::vector<RoleName>& roles) {
+ for (size_t i = 0; i < roles.size(); ++i) {
+ if (!authzSession->isAuthorizedToRevokeRole(roles[i])) {
+ return Status(ErrorCodes::Unauthorized,
+ str::stream() << "Not authorized to revoke role: " <<
+ roles[i].getFullName());
+ }
+ }
+ return Status::OK();
+ }
+
+ Status checkAuthorizedToRevokePrivileges(AuthorizationSession* authzSession,
+ const PrivilegeVector& privileges) {
+ for (PrivilegeVector::const_iterator it = privileges.begin();
+ it != privileges.end(); ++it) {
+ Status status = authzSession->checkAuthorizedToRevokePrivilege(*it);
+ if (!status.isOK()) {
+ return status;
+ }
+ }
+
+ return Status::OK();
+ }
+
+ Status checkAuthForCreateUserCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
+ AuthorizationSession* authzSession = AuthorizationSession::get(client);
+ auth::CreateOrUpdateUserArgs args;
+ Status status = auth::parseCreateOrUpdateUserCommands(cmdObj,
+ "createUser",
+ dbname,
+ &args);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ if (!authzSession->isAuthorizedForActionsOnResource(
+ ResourcePattern::forDatabaseName(args.userName.getDB()),
+ ActionType::createUser)) {
+ return Status(ErrorCodes::Unauthorized,
+ str::stream() << "Not authorized to create users on db: "
+ << args.userName.getDB());
+ }
+
+ return checkAuthorizedToGrantRoles(authzSession, args.roles);
+ }
+
+ Status checkAuthForUpdateUserCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
+ AuthorizationSession* authzSession = AuthorizationSession::get(client);
+ auth::CreateOrUpdateUserArgs args;
+ Status status = auth::parseCreateOrUpdateUserCommands(cmdObj,
+ "updateUser",
+ dbname,
+ &args);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ if (args.hasHashedPassword) {
+ if (!authzSession->isAuthorizedToChangeOwnPasswordAsUser(args.userName) &&
+ !authzSession->isAuthorizedForActionsOnResource(
+ ResourcePattern::forDatabaseName(args.userName.getDB()),
+ ActionType::changePassword)) {
+ return Status(ErrorCodes::Unauthorized,
+ str::stream() << "Not authorized to change password of user: "
+ << args.userName.getFullName());
+ }
+ }
+
+ if (args.hasCustomData) {
+ if (!authzSession->isAuthorizedToChangeOwnCustomDataAsUser(args.userName) &&
+ !authzSession->isAuthorizedForActionsOnResource(
+ ResourcePattern::forDatabaseName(args.userName.getDB()),
+ ActionType::changeCustomData)) {
+ return Status(ErrorCodes::Unauthorized,
+ str::stream() << "Not authorized to change customData of user: "
+ << args.userName.getFullName());
+ }
+ }
+
+ if (args.hasRoles) {
+ // You don't know what roles you might be revoking, so require the ability to
+ // revoke any role in the system.
+ if (!authzSession->isAuthorizedForActionsOnResource(
+ ResourcePattern::forAnyNormalResource(), ActionType::revokeRole)) {
+ return Status(ErrorCodes::Unauthorized,
+ "In order to use updateUser to set roles array, must be "
+ "authorized to revoke any role in the system");
+ }
+
+ return checkAuthorizedToGrantRoles(authzSession, args.roles);
+ }
+
+ return Status::OK();
+ }
+
+ Status checkAuthForGrantRolesToUserCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
+ AuthorizationSession* authzSession = AuthorizationSession::get(client);
+ std::vector<RoleName> roles;
+ std::string unusedUserNameString;
+ BSONObj unusedWriteConcern;
+ Status status = auth::parseRolePossessionManipulationCommands(cmdObj,
+ "grantRolesToUser",
+ dbname,
+ &unusedUserNameString,
+ &roles,
+ &unusedWriteConcern);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ return checkAuthorizedToGrantRoles(authzSession, roles);
+ }
+
+ Status checkAuthForCreateRoleCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
+ AuthorizationSession* authzSession = AuthorizationSession::get(client);
+ auth::CreateOrUpdateRoleArgs args;
+ Status status = auth::parseCreateOrUpdateRoleCommands(cmdObj,
+ "createRole",
+ dbname,
+ &args);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ if (!authzSession->isAuthorizedForActionsOnResource(
+ ResourcePattern::forDatabaseName(args.roleName.getDB()),
+ ActionType::createRole)) {
+ return Status(ErrorCodes::Unauthorized,
+ str::stream() << "Not authorized to create roles on db: "
+ << args.roleName.getDB());
+ }
+
+ status = checkAuthorizedToGrantRoles(authzSession, args.roles);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ return checkAuthorizedToGrantPrivileges(authzSession, args.privileges);
+ }
+
+ Status checkAuthForUpdateRoleCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
+ AuthorizationSession* authzSession = AuthorizationSession::get(client);
+ auth::CreateOrUpdateRoleArgs args;
+ Status status = auth::parseCreateOrUpdateRoleCommands(cmdObj,
+ "updateRole",
+ dbname,
+ &args);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ // You don't know what roles or privileges you might be revoking, so require the ability
+ // to revoke any role (or privilege) in the system.
+ if (!authzSession->isAuthorizedForActionsOnResource(
+ ResourcePattern::forAnyNormalResource(), ActionType::revokeRole)) {
+ return Status(ErrorCodes::Unauthorized,
+ "updateRole command required the ability to revoke any role in the "
+ "system");
+ }
+
+ status = checkAuthorizedToGrantRoles(authzSession, args.roles);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ return checkAuthorizedToGrantPrivileges(authzSession, args.privileges);
+ }
+
+ Status checkAuthForGrantRolesToRoleCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
+ AuthorizationSession* authzSession = AuthorizationSession::get(client);
+ std::vector<RoleName> roles;
+ std::string unusedUserNameString;
+ BSONObj unusedWriteConcern;
+ Status status = auth::parseRolePossessionManipulationCommands(cmdObj,
+ "grantRolesToRole",
+ dbname,
+ &unusedUserNameString,
+ &roles,
+ &unusedWriteConcern);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ return checkAuthorizedToGrantRoles(authzSession, roles);
+ }
+
+ Status checkAuthForGrantPrivilegesToRoleCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
+ AuthorizationSession* authzSession = AuthorizationSession::get(client);
+ PrivilegeVector privileges;
+ RoleName unusedRoleName;
+ BSONObj unusedWriteConcern;
+ Status status =
+ auth::parseAndValidateRolePrivilegeManipulationCommands(cmdObj,
+ "grantPrivilegesToRole",
+ dbname,
+ &unusedRoleName,
+ &privileges,
+ &unusedWriteConcern);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ return checkAuthorizedToGrantPrivileges(authzSession, privileges);
+ }
+
+ Status checkAuthForDropUserCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
+ AuthorizationSession* authzSession = AuthorizationSession::get(client);
+ UserName userName;
+ BSONObj unusedWriteConcern;
+ Status status = auth::parseAndValidateDropUserCommand(cmdObj,
+ dbname,
+ &userName,
+ &unusedWriteConcern);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ if (!authzSession->isAuthorizedForActionsOnResource(
+ ResourcePattern::forDatabaseName(userName.getDB()), ActionType::dropUser)) {
+ return Status(ErrorCodes::Unauthorized,
+ str::stream() << "Not authorized to drop users from the "
+ << userName.getDB() << " database");
+ }
+ return Status::OK();
+ }
+
+ Status checkAuthForDropRoleCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
+ AuthorizationSession* authzSession = AuthorizationSession::get(client);
+ RoleName roleName;
+ BSONObj unusedWriteConcern;
+ Status status = auth::parseDropRoleCommand(cmdObj,
+ dbname,
+ &roleName,
+ &unusedWriteConcern);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ if (!authzSession->isAuthorizedForActionsOnResource(
+ ResourcePattern::forDatabaseName(roleName.getDB()), ActionType::dropRole)) {
+ return Status(ErrorCodes::Unauthorized,
+ str::stream() << "Not authorized to drop roles from the "
+ << roleName.getDB() << " database");
+ }
+ return Status::OK();
+ }
+
+ Status checkAuthForDropAllUsersFromDatabaseCommand(ClientBasic* client,
+ const std::string& dbname) {
+ AuthorizationSession* authzSession = AuthorizationSession::get(client);
+ if (!authzSession->isAuthorizedForActionsOnResource(
+ ResourcePattern::forDatabaseName(dbname), ActionType::dropUser)) {
+ return Status(ErrorCodes::Unauthorized,
+ str::stream() << "Not authorized to drop users from the "
+ << dbname << " database");
+ }
+ return Status::OK();
+ }
+
+ Status checkAuthForRevokeRolesFromUserCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
+ AuthorizationSession* authzSession = AuthorizationSession::get(client);
+ std::vector<RoleName> roles;
+ std::string unusedUserNameString;
+ BSONObj unusedWriteConcern;
+ Status status = auth::parseRolePossessionManipulationCommands(cmdObj,
+ "revokeRolesFromUser",
+ dbname,
+ &unusedUserNameString,
+ &roles,
+ &unusedWriteConcern);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ return checkAuthorizedToRevokeRoles(authzSession, roles);
+ }
+
+ Status checkAuthForRevokeRolesFromRoleCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
+ AuthorizationSession* authzSession = AuthorizationSession::get(client);
+ std::vector<RoleName> roles;
+ std::string unusedUserNameString;
+ BSONObj unusedWriteConcern;
+ Status status = auth::parseRolePossessionManipulationCommands(cmdObj,
+ "revokeRolesFromRole",
+ dbname,
+ &unusedUserNameString,
+ &roles,
+ &unusedWriteConcern);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ return checkAuthorizedToRevokeRoles(authzSession, roles);
+ }
+
+ Status checkAuthForUsersInfoCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
+ AuthorizationSession* authzSession = AuthorizationSession::get(client);
+ auth::UsersInfoArgs args;
+ Status status = auth::parseUsersInfoCommand(cmdObj, dbname, &args);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ if (args.allForDB) {
+ if (!authzSession->isAuthorizedForActionsOnResource(
+ ResourcePattern::forDatabaseName(dbname), ActionType::viewUser)) {
+ return Status(ErrorCodes::Unauthorized,
+ str::stream() << "Not authorized to view users from the "
+ << dbname << " database");
+ }
+ } else {
+ for (size_t i = 0; i < args.userNames.size(); ++i) {
+ if (authzSession->lookupUser(args.userNames[i])) {
+ continue; // Can always view users you are logged in as
+ }
+ if (!authzSession->isAuthorizedForActionsOnResource(
+ ResourcePattern::forDatabaseName(args.userNames[i].getDB()),
+ ActionType::viewUser)) {
+ return Status(ErrorCodes::Unauthorized,
+ str::stream() << "Not authorized to view users from the "
+ << dbname << " database");
+ }
+ }
+ }
+ return Status::OK();
+ }
+
+ Status checkAuthForRevokePrivilegesFromRoleCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
+ AuthorizationSession* authzSession = AuthorizationSession::get(client);
+ PrivilegeVector privileges;
+ RoleName unusedRoleName;
+ BSONObj unusedWriteConcern;
+ Status status =
+ auth::parseAndValidateRolePrivilegeManipulationCommands(cmdObj,
+ "revokePrivilegesFromRole",
+ dbname,
+ &unusedRoleName,
+ &privileges,
+ &unusedWriteConcern);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ return checkAuthorizedToRevokePrivileges(authzSession, privileges);
+ }
+
+ Status checkAuthForDropAllRolesFromDatabaseCommand(ClientBasic* client,
+ const std::string& dbname) {
+ AuthorizationSession* authzSession = AuthorizationSession::get(client);
+ if (!authzSession->isAuthorizedForActionsOnResource(
+ ResourcePattern::forDatabaseName(dbname), ActionType::dropRole)) {
+ return Status(ErrorCodes::Unauthorized,
+ str::stream() << "Not authorized to drop roles from the "
+ << dbname << " database");
+ }
+ return Status::OK();
+ }
+
+ Status checkAuthForRolesInfoCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
+ AuthorizationSession* authzSession = AuthorizationSession::get(client);
+ auth::RolesInfoArgs args;
+ Status status = auth::parseRolesInfoCommand(cmdObj, dbname, &args);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ if (args.allForDB) {
+ if (!authzSession->isAuthorizedForActionsOnResource(
+ ResourcePattern::forDatabaseName(dbname), ActionType::viewRole)) {
+ return Status(ErrorCodes::Unauthorized,
+ str::stream() << "Not authorized to view roles from the "
+ << dbname << " database");
+ }
+ } else {
+ for (size_t i = 0; i < args.roleNames.size(); ++i) {
+ if (authzSession->isAuthenticatedAsUserWithRole(args.roleNames[i])) {
+ continue; // Can always see roles that you are a member of
+ }
+
+ if (!authzSession->isAuthorizedForActionsOnResource(
+ ResourcePattern::forDatabaseName(args.roleNames[i].getDB()),
+ ActionType::viewRole)) {
+ return Status(ErrorCodes::Unauthorized,
+ str::stream() << "Not authorized to view roles from the "
+ << args.roleNames[i].getDB() << " database");
+ }
+ }
+ }
+
+ return Status::OK();
+ }
+
+ Status checkAuthForInvalidateUserCacheCommand(ClientBasic* client) {
+ AuthorizationSession* authzSession = AuthorizationSession::get(client);
+ if (!authzSession->isAuthorizedForActionsOnResource(
+ ResourcePattern::forClusterResource(), ActionType::invalidateUserCache)) {
+ return Status(ErrorCodes::Unauthorized, "Not authorized to invalidate user cache");
+ }
+ return Status::OK();
+ }
+
+ Status checkAuthForGetUserCacheGenerationCommand(ClientBasic* client) {
+ AuthorizationSession* authzSession = AuthorizationSession::get(client);
+ if (!authzSession->isAuthorizedForActionsOnResource(
+ ResourcePattern::forClusterResource(), ActionType::internal)) {
+ return Status(ErrorCodes::Unauthorized, "Not authorized to get cache generation");
+ }
+ return Status::OK();
+ }
+
+ Status checkAuthForMergeAuthzCollectionsCommand(ClientBasic* client,
+ const BSONObj& cmdObj) {
+ auth::MergeAuthzCollectionsArgs args;
+ Status status = auth::parseMergeAuthzCollectionsCommand(cmdObj, &args);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ AuthorizationSession* authzSession = AuthorizationSession::get(client);
+ ActionSet actions;
+ actions.addAction(ActionType::createUser);
+ actions.addAction(ActionType::createRole);
+ actions.addAction(ActionType::grantRole);
+ actions.addAction(ActionType::revokeRole);
+ if (args.drop) {
+ actions.addAction(ActionType::dropUser);
+ actions.addAction(ActionType::dropRole);
+ }
+ if (!authzSession->isAuthorizedForActionsOnResource(
+ ResourcePattern::forAnyNormalResource(), actions)) {
+ return Status(ErrorCodes::Unauthorized,
+ "Not authorized to update user/role data using _mergeAuthzCollections"
+ " command");
+ }
+ if (!args.usersCollName.empty() &&
+ !authzSession->isAuthorizedForActionsOnResource(
+ ResourcePattern::forExactNamespace(NamespaceString(args.usersCollName)),
+ ActionType::find)) {
+ return Status(ErrorCodes::Unauthorized,
+ str::stream() << "Not authorized to read "
+ << args.usersCollName);
+ }
+ if (!args.rolesCollName.empty() &&
+ !authzSession->isAuthorizedForActionsOnResource(
+ ResourcePattern::forExactNamespace(NamespaceString(args.rolesCollName)),
+ ActionType::find)) {
+ return Status(ErrorCodes::Unauthorized,
+ str::stream() << "Not authorized to read "
+ << args.rolesCollName);
+ }
+ return Status::OK();
+ }
+
+
+} // namespace auth
+} // namespace mongo
diff --git a/src/mongo/s/catalog/catalog_manager.h b/src/mongo/s/catalog/catalog_manager.h
index 8d9598a120d..bc5e1608dc9 100644
--- a/src/mongo/s/catalog/catalog_manager.h
+++ b/src/mongo/s/catalog/catalog_manager.h
@@ -41,6 +41,7 @@ namespace mongo {
class BatchedCommandResponse;
struct BSONArray;
class BSONObj;
+ class BSONObjBuilder;
class ChunkType;
class CollectionType;
class ConnectionString;
@@ -251,6 +252,26 @@ namespace mongo {
virtual bool doShardsExist() = 0;
/**
+ * Runs a user management command on the config servers.
+ * @param commandName: name of command
+ * @param dbname: database for which the user management command is invoked
+ * @param cmdObj: command obj
+ * @param result: contains data returned from config servers
+ * Returns true on success.
+ */
+ virtual bool runUserManagementWriteCommand(const std::string& commandName,
+ const std::string& dbname,
+ const BSONObj& cmdObj,
+ BSONObjBuilder* result) = 0;
+
+ /**
+ * Runs a read-only user management command on a single config server.
+ */
+ virtual bool runUserManagementReadCommand(const std::string& dbname,
+ const BSONObj& cmdObj,
+ BSONObjBuilder* result) = 0;
+
+ /**
* Applies oplog entries to the config servers.
* Used by mergeChunk, splitChunk, and moveChunk commands.
*
diff --git a/src/mongo/s/catalog/legacy/catalog_manager_legacy.cpp b/src/mongo/s/catalog/legacy/catalog_manager_legacy.cpp
index f4fb009e9df..88c1c57c21f 100644
--- a/src/mongo/s/catalog/legacy/catalog_manager_legacy.cpp
+++ b/src/mongo/s/catalog/legacy/catalog_manager_legacy.cpp
@@ -43,9 +43,11 @@
#include "mongo/client/replica_set_monitor.h"
#include "mongo/db/audit.h"
#include "mongo/db/client.h"
+#include "mongo/db/commands.h"
#include "mongo/db/operation_context.h"
#include "mongo/db/server_options.h"
#include "mongo/platform/atomic_word.h"
+#include "mongo/s/bson_serializable.h"
#include "mongo/s/catalog/legacy/cluster_client_internal.h"
#include "mongo/s/catalog/legacy/config_coordinator.h"
#include "mongo/s/catalog/type_actionlog.h"
@@ -1213,6 +1215,107 @@ namespace {
return _getShardCount() > 0;
}
+ bool CatalogManagerLegacy::runUserManagementWriteCommand(const string& commandName,
+ const string& dbname,
+ const BSONObj& cmdObj,
+ BSONObjBuilder* result) {
+ DBClientMultiCommand dispatcher;
+ RawBSONSerializable requestCmdSerial(cmdObj);
+ for (const ConnectionString& configServer : _configServers) {
+ dispatcher.addCommand(configServer, dbname, requestCmdSerial);
+ }
+
+ auto scopedDistLock = getDistLockManager()->lock("authorizationData",
+ commandName,
+ stdx::chrono::milliseconds(5000));
+ if (!scopedDistLock.isOK()) {
+ return Command::appendCommandStatus(*result, scopedDistLock.getStatus());
+ }
+
+ dispatcher.sendAll();
+
+ BSONObj responseObj;
+
+ Status prevStatus{Status::OK()};
+ Status currStatus{Status::OK()};
+
+ BSONObjBuilder responses;
+ unsigned failedCount = 0;
+ bool sameError = true;
+ while (dispatcher.numPending() > 0) {
+ ConnectionString configServer;
+ RawBSONSerializable responseCmdSerial;
+
+ Status dispatchStatus = dispatcher.recvAny(&configServer,
+ &responseCmdSerial);
+
+ if (!dispatchStatus.isOK()) {
+ return Command::appendCommandStatus(*result, dispatchStatus);
+ }
+
+ responseObj = responseCmdSerial.toBSON();
+ responses.append(configServer.toString(), responseObj);
+
+ currStatus = Command::getStatusFromCommandResult(responseObj);
+ if (!currStatus.isOK()) {
+ // same error <=> adjacent error statuses are the same
+ if (failedCount > 0 && prevStatus != currStatus) {
+ sameError = false;
+ }
+ failedCount++;
+ prevStatus = currStatus;
+ }
+ }
+
+ if (failedCount == 0) {
+ result->appendElements(responseObj);
+ return true;
+ }
+
+ // if the command succeeds on at least one config server and fails on at least one,
+ // manual intervention is required
+ if (failedCount < _configServers.size()) {
+ Status status(ErrorCodes::ManualInterventionRequired,
+ str::stream() << "Config write was not consistent - "
+ << "user management command failed on at least one "
+ << "config server but passed on at least one other. "
+ << "Manual intervention may be required. "
+ << "Config responses: " << responses.obj().toString());
+ return Command::appendCommandStatus(*result, status);
+ }
+
+ if (sameError) {
+ result->appendElements(responseObj);
+ return false;
+ }
+
+ Status status(ErrorCodes::ManualInterventionRequired,
+ str::stream() << "Config write was not consistent - "
+ << "user management command produced inconsistent results. "
+ << "Manual intervention may be required. "
+ << "Config responses: " << responses.obj().toString());
+ return Command::appendCommandStatus(*result, status);
+ }
+
+ bool CatalogManagerLegacy::runUserManagementReadCommand(const string& dbname,
+ const BSONObj& cmdObj,
+ BSONObjBuilder* result) {
+ try {
+ // let SyncClusterConnection handle connecting to the first config server
+ // that is reachable and returns data
+ ScopedDbConnection conn(_configServerConnectionString, 30);
+
+ BSONObj cmdResult;
+ const bool ok = conn->runCommand(dbname, cmdObj, cmdResult);
+ result->appendElements(cmdResult);
+ conn.done();
+ return ok;
+ }
+ catch (const DBException& ex) {
+ return Command::appendCommandStatus(*result, ex.toStatus());
+ }
+ }
+
Status CatalogManagerLegacy::applyChunkOpsDeprecated(const BSONArray& updateOps,
const BSONArray& preCondition) {
BSONObj cmd = BSON("applyOps" << updateOps <<
diff --git a/src/mongo/s/catalog/legacy/catalog_manager_legacy.h b/src/mongo/s/catalog/legacy/catalog_manager_legacy.h
index ac9ee02ab94..9be92345517 100644
--- a/src/mongo/s/catalog/legacy/catalog_manager_legacy.h
+++ b/src/mongo/s/catalog/legacy/catalog_manager_legacy.h
@@ -108,6 +108,18 @@ namespace mongo {
virtual bool doShardsExist();
+ /**
+ * Grabs a distributed lock and runs the command on all config servers.
+ */
+ virtual bool runUserManagementWriteCommand(const std::string& commandName,
+ const std::string& dbname,
+ const BSONObj& cmdObj,
+ BSONObjBuilder* result);
+
+ virtual bool runUserManagementReadCommand(const std::string& dbname,
+ const BSONObj& cmdObj,
+ BSONObjBuilder* result);
+
virtual Status applyChunkOpsDeprecated(const BSONArray& updateOps,
const BSONArray& preCondition);
diff --git a/src/mongo/s/commands/SConscript b/src/mongo/s/commands/SConscript
index 59ea19f71c1..a32160a676a 100644
--- a/src/mongo/s/commands/SConscript
+++ b/src/mongo/s/commands/SConscript
@@ -38,6 +38,7 @@ env.Library(
'cluster_shard_collection_cmd.cpp',
'cluster_shutdown_cmd.cpp',
'cluster_split_collection_cmd.cpp',
+ 'cluster_user_management_commands.cpp',
'cluster_whats_my_uri_cmd.cpp',
'cluster_write_cmd.cpp',
'commands_public.cpp',
diff --git a/src/mongo/s/commands/cluster_user_management_commands.cpp b/src/mongo/s/commands/cluster_user_management_commands.cpp
new file mode 100644
index 00000000000..2c781ac0e91
--- /dev/null
+++ b/src/mongo/s/commands/cluster_user_management_commands.cpp
@@ -0,0 +1,791 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kAccessControl
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/commands/user_management_commands.h"
+
+#include "mongo/base/status.h"
+#include "mongo/bson/mutable/document.h"
+#include "mongo/db/auth/authorization_manager.h"
+#include "mongo/db/auth/authorization_manager_global.h"
+#include "mongo/db/auth/user_management_commands_parser.h"
+#include "mongo/client/dbclientinterface.h"
+#include "mongo/config.h"
+#include "mongo/db/commands.h"
+#include "mongo/db/jsobj.h"
+#include "mongo/s/catalog/catalog_manager.h"
+#include "mongo/s/grid.h"
+
+namespace mongo {
+
+ using std::string;
+ using std::stringstream;
+ using std::vector;
+
+ class CmdCreateUser : public Command {
+ public:
+
+ CmdCreateUser() : Command("createUser") {}
+
+ virtual bool slaveOk() const { return false; }
+
+ virtual bool isWriteCommandForConfigServer() const { return false; }
+
+ virtual void help(stringstream& ss) const {
+ ss << "Adds a user to the system";
+ }
+
+ virtual Status checkAuthForCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
+ return auth::checkAuthForCreateUserCommand(client, dbname, cmdObj);
+ }
+
+ bool run(OperationContext* txn,
+ const string& dbname,
+ BSONObj& cmdObj,
+ int options,
+ string& errmsg,
+ BSONObjBuilder& result) {
+ return grid.catalogManager()->runUserManagementWriteCommand(this->name,
+ dbname,
+ cmdObj,
+ &result);
+ }
+
+ virtual void redactForLogging(mutablebson::Document* cmdObj) {
+ auth::redactPasswordData(cmdObj->root());
+ }
+
+ } cmdCreateUser;
+
+ class CmdUpdateUser : public Command {
+ public:
+
+ CmdUpdateUser() : Command("updateUser") {}
+
+ virtual bool slaveOk() const { return false; }
+
+ virtual bool isWriteCommandForConfigServer() const { return false; }
+
+ virtual void help(stringstream& ss) const {
+ ss << "Used to update a user, for example to change its password";
+ }
+
+ virtual Status checkAuthForCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
+ return auth::checkAuthForUpdateUserCommand(client, dbname, cmdObj);
+ }
+
+ bool run(OperationContext* txn,
+ const string& dbname,
+ BSONObj& cmdObj,
+ int options,
+ string& errmsg,
+ BSONObjBuilder& result) {
+ auth::CreateOrUpdateUserArgs args;
+ Status status = auth::parseCreateOrUpdateUserCommands(cmdObj,
+ this->name,
+ dbname,
+ &args);
+ if (!status.isOK()) {
+ return appendCommandStatus(result, status);
+ }
+ const bool ok = grid.catalogManager()->runUserManagementWriteCommand(this->name,
+ dbname,
+ cmdObj,
+ &result);
+
+ AuthorizationManager* authzManager = getGlobalAuthorizationManager();
+ invariant(authzManager);
+ authzManager->invalidateUserByName(args.userName);
+
+ return ok;
+ }
+
+ virtual void redactForLogging(mutablebson::Document* cmdObj) {
+ auth::redactPasswordData(cmdObj->root());
+ }
+
+ } cmdUpdateUser;
+
+ class CmdDropUser : public Command {
+ public:
+
+ CmdDropUser() : Command("dropUser") {}
+
+ virtual bool slaveOk() const { return false; }
+
+ virtual bool isWriteCommandForConfigServer() const { return false; }
+
+ virtual void help(stringstream& ss) const {
+ ss << "Drops a single user.";
+ }
+
+ virtual Status checkAuthForCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
+ return auth::checkAuthForDropUserCommand(client, dbname, cmdObj);
+ }
+
+ bool run(OperationContext* txn,
+ const string& dbname,
+ BSONObj& cmdObj,
+ int options,
+ string& errmsg,
+ BSONObjBuilder& result) {
+ UserName userName;
+ BSONObj unusedWriteConcern;
+ Status status = auth::parseAndValidateDropUserCommand(cmdObj,
+ dbname,
+ &userName,
+ &unusedWriteConcern);
+ if (!status.isOK()) {
+ return appendCommandStatus(result, status);
+ }
+ const bool ok = grid.catalogManager()->runUserManagementWriteCommand(this->name,
+ dbname,
+ cmdObj,
+ &result);
+
+ AuthorizationManager* authzManager = getGlobalAuthorizationManager();
+ invariant(authzManager);
+ authzManager->invalidateUserByName(userName);
+
+ return ok;
+ }
+
+ } cmdDropUser;
+
+ class CmdDropAllUsersFromDatabase : public Command {
+ public:
+
+ CmdDropAllUsersFromDatabase() : Command("dropAllUsersFromDatabase") {}
+
+ virtual bool slaveOk() const { return false; }
+
+ virtual bool isWriteCommandForConfigServer() const { return false; }
+
+ virtual void help(stringstream& ss) const {
+ ss << "Drops all users for a single database.";
+ }
+
+ virtual Status checkAuthForCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
+ return auth::checkAuthForDropAllUsersFromDatabaseCommand(client, dbname);
+ }
+
+ bool run(OperationContext* txn,
+ const string& dbname,
+ BSONObj& cmdObj,
+ int options,
+ string& errmsg,
+ BSONObjBuilder& result) {
+ const bool ok = grid.catalogManager()->runUserManagementWriteCommand(this->name,
+ dbname,
+ cmdObj,
+ &result);
+
+ AuthorizationManager* authzManager = getGlobalAuthorizationManager();
+ invariant(authzManager);
+ authzManager->invalidateUsersFromDB(dbname);
+
+ return ok;
+ }
+
+ } cmdDropAllUsersFromDatabase;
+
+ class CmdGrantRolesToUser: public Command {
+ public:
+
+ CmdGrantRolesToUser() : Command("grantRolesToUser") {}
+
+ virtual bool slaveOk() const { return false; }
+
+ virtual bool isWriteCommandForConfigServer() const { return false; }
+
+ virtual void help(stringstream& ss) const {
+ ss << "Grants roles to a user.";
+ }
+
+ virtual Status checkAuthForCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
+ return auth::checkAuthForGrantRolesToUserCommand(client, dbname, cmdObj);
+ }
+
+ bool run(OperationContext* txn,
+ const string& dbname,
+ BSONObj& cmdObj,
+ int options,
+ string& errmsg,
+ BSONObjBuilder& result) {
+ string userNameString;
+ vector<RoleName> roles;
+ BSONObj unusedWriteConcern;
+ Status status = auth::parseRolePossessionManipulationCommands(cmdObj,
+ this->name,
+ dbname,
+ &userNameString,
+ &roles,
+ &unusedWriteConcern);
+ if (!status.isOK()) {
+ return appendCommandStatus(result, status);
+ }
+ const bool ok = grid.catalogManager()->runUserManagementWriteCommand(this->name,
+ dbname,
+ cmdObj,
+ &result);
+
+ AuthorizationManager* authzManager = getGlobalAuthorizationManager();
+ invariant(authzManager);
+ authzManager->invalidateUserByName(UserName(userNameString, dbname));
+
+ return ok;
+ }
+
+ } cmdGrantRolesToUser;
+
+ class CmdRevokeRolesFromUser: public Command {
+ public:
+
+ CmdRevokeRolesFromUser() : Command("revokeRolesFromUser") {}
+
+ virtual bool slaveOk() const { return false; }
+
+ virtual bool isWriteCommandForConfigServer() const { return false; }
+
+ virtual void help(stringstream& ss) const {
+ ss << "Revokes roles from a user.";
+ }
+
+ virtual Status checkAuthForCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
+ return auth::checkAuthForRevokeRolesFromUserCommand(client, dbname, cmdObj);
+ }
+
+ bool run(OperationContext* txn,
+ const string& dbname,
+ BSONObj& cmdObj,
+ int options,
+ string& errmsg,
+ BSONObjBuilder& result) {
+ string userNameString;
+ vector<RoleName> unusedRoles;
+ BSONObj unusedWriteConcern;
+ Status status = auth::parseRolePossessionManipulationCommands(cmdObj,
+ this->name,
+ dbname,
+ &userNameString,
+ &unusedRoles,
+ &unusedWriteConcern);
+ if (!status.isOK()) {
+ return appendCommandStatus(result, status);
+ }
+ const bool ok = grid.catalogManager()->runUserManagementWriteCommand(this->name,
+ dbname,
+ cmdObj,
+ &result);
+
+ AuthorizationManager* authzManager = getGlobalAuthorizationManager();
+ invariant(authzManager);
+ authzManager->invalidateUserByName(UserName(userNameString, dbname));
+
+ return ok;
+ }
+
+ } cmdRevokeRolesFromUser;
+
+ class CmdUsersInfo: public Command {
+ public:
+
+ virtual bool slaveOk() const { return false; }
+
+ virtual bool slaveOverrideOk() const { return true; }
+
+ virtual bool isWriteCommandForConfigServer() const { return false; }
+
+ CmdUsersInfo() : Command("usersInfo") {}
+
+ virtual void help(stringstream& ss) const {
+ ss << "Returns information about users.";
+ }
+
+ virtual Status checkAuthForCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
+ return auth::checkAuthForUsersInfoCommand(client, dbname, cmdObj);
+ }
+
+ bool run(OperationContext* txn,
+ const string& dbname,
+ BSONObj& cmdObj,
+ int options,
+ string& errmsg,
+ BSONObjBuilder& result) {
+ return grid.catalogManager()->runUserManagementReadCommand(dbname,
+ cmdObj,
+ &result);
+ }
+
+ } cmdUsersInfo;
+
+ class CmdCreateRole: public Command {
+ public:
+
+ CmdCreateRole() : Command("createRole") {}
+
+ virtual bool slaveOk() const { return false; }
+
+ virtual bool isWriteCommandForConfigServer() const { return false; }
+
+ virtual void help(stringstream& ss) const {
+ ss << "Adds a role to the system";
+ }
+
+ virtual Status checkAuthForCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
+ return auth::checkAuthForCreateRoleCommand(client, dbname, cmdObj);
+ }
+
+ bool run(OperationContext* txn,
+ const string& dbname,
+ BSONObj& cmdObj,
+ int options,
+ string& errmsg,
+ BSONObjBuilder& result) {
+ return grid.catalogManager()->runUserManagementWriteCommand(this->name,
+ dbname,
+ cmdObj,
+ &result);
+ }
+
+ } cmdCreateRole;
+
+ class CmdUpdateRole: public Command {
+ public:
+
+ CmdUpdateRole() : Command("updateRole") {}
+
+ virtual bool slaveOk() const { return false; }
+
+ virtual bool isWriteCommandForConfigServer() const { return false; }
+
+ virtual void help(stringstream& ss) const {
+ ss << "Used to update a role";
+ }
+
+ virtual Status checkAuthForCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
+ return auth::checkAuthForUpdateRoleCommand(client, dbname, cmdObj);
+ }
+
+ bool run(OperationContext* txn,
+ const string& dbname,
+ BSONObj& cmdObj,
+ int options,
+ string& errmsg,
+ BSONObjBuilder& result) {
+ const bool ok = grid.catalogManager()->runUserManagementWriteCommand(this->name,
+ dbname,
+ cmdObj,
+ &result);
+
+ AuthorizationManager* authzManager = getGlobalAuthorizationManager();
+ invariant(authzManager);
+ authzManager->invalidateUserCache();
+
+ return ok;
+ }
+
+ } cmdUpdateRole;
+
+ class CmdGrantPrivilegesToRole: public Command {
+ public:
+
+ CmdGrantPrivilegesToRole() : Command("grantPrivilegesToRole") {}
+
+ virtual bool slaveOk() const { return false; }
+
+ virtual bool isWriteCommandForConfigServer() const { return false; }
+
+ virtual void help(stringstream& ss) const {
+ ss << "Grants privileges to a role";
+ }
+
+ virtual Status checkAuthForCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
+ return auth::checkAuthForGrantPrivilegesToRoleCommand(client, dbname, cmdObj);
+ }
+
+ bool run(OperationContext* txn,
+ const string& dbname,
+ BSONObj& cmdObj,
+ int options,
+ string& errmsg,
+ BSONObjBuilder& result) {
+ const bool ok = grid.catalogManager()->runUserManagementWriteCommand(this->name,
+ dbname,
+ cmdObj,
+ &result);
+
+ AuthorizationManager* authzManager = getGlobalAuthorizationManager();
+ invariant(authzManager);
+ authzManager->invalidateUserCache();
+
+ return ok;
+ }
+
+ } cmdGrantPrivilegesToRole;
+
+ class CmdRevokePrivilegesFromRole: public Command {
+ public:
+
+ CmdRevokePrivilegesFromRole() : Command("revokePrivilegesFromRole") {}
+
+ virtual bool slaveOk() const { return false; }
+
+ virtual bool isWriteCommandForConfigServer() const { return false; }
+
+ virtual void help(stringstream& ss) const {
+ ss << "Revokes privileges from a role";
+ }
+
+ virtual Status checkAuthForCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
+ return auth::checkAuthForRevokePrivilegesFromRoleCommand(client, dbname, cmdObj);
+ }
+
+ bool run(OperationContext* txn,
+ const string& dbname,
+ BSONObj& cmdObj,
+ int options,
+ string& errmsg,
+ BSONObjBuilder& result) {
+ const bool ok = grid.catalogManager()->runUserManagementWriteCommand(this->name,
+ dbname,
+ cmdObj,
+ &result);
+
+ AuthorizationManager* authzManager = getGlobalAuthorizationManager();
+ invariant(authzManager);
+ authzManager->invalidateUserCache();
+
+ return ok;
+ }
+
+ } cmdRevokePrivilegesFromRole;
+
+ class CmdGrantRolesToRole: public Command {
+ public:
+
+ CmdGrantRolesToRole() : Command("grantRolesToRole") {}
+
+ virtual bool slaveOk() const { return false; }
+
+ virtual bool isWriteCommandForConfigServer() const { return false; }
+
+ virtual void help(stringstream& ss) const {
+ ss << "Grants roles to another role.";
+ }
+
+ virtual Status checkAuthForCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
+ return auth::checkAuthForGrantRolesToRoleCommand(client, dbname, cmdObj);
+ }
+
+ bool run(OperationContext* txn,
+ const string& dbname,
+ BSONObj& cmdObj,
+ int options,
+ string& errmsg,
+ BSONObjBuilder& result) {
+ const bool ok = grid.catalogManager()->runUserManagementWriteCommand(this->name,
+ dbname,
+ cmdObj,
+ &result);
+
+ AuthorizationManager* authzManager = getGlobalAuthorizationManager();
+ invariant(authzManager);
+ authzManager->invalidateUserCache();
+
+ return ok;
+ }
+
+ } cmdGrantRolesToRole;
+
+ class CmdRevokeRolesFromRole: public Command {
+ public:
+
+ CmdRevokeRolesFromRole() : Command("revokeRolesFromRole") {}
+
+ virtual bool slaveOk() const { return false; }
+
+ virtual bool isWriteCommandForConfigServer() const { return false; }
+
+ virtual void help(stringstream& ss) const {
+ ss << "Revokes roles from another role.";
+ }
+
+ virtual Status checkAuthForCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
+ return auth::checkAuthForRevokeRolesFromRoleCommand(client, dbname, cmdObj);
+ }
+
+ bool run(OperationContext* txn,
+ const string& dbname,
+ BSONObj& cmdObj,
+ int options,
+ string& errmsg,
+ BSONObjBuilder& result) {
+ const bool ok = grid.catalogManager()->runUserManagementWriteCommand(this->name,
+ dbname,
+ cmdObj,
+ &result);
+
+ AuthorizationManager* authzManager = getGlobalAuthorizationManager();
+ invariant(authzManager);
+ authzManager->invalidateUserCache();
+
+ return ok;
+ }
+
+ } cmdRevokeRolesFromRole;
+
+ class CmdDropRole: public Command {
+ public:
+
+ CmdDropRole() : Command("dropRole") {}
+
+ virtual bool slaveOk() const { return false; }
+
+ virtual bool isWriteCommandForConfigServer() const { return false; }
+
+ virtual void help(stringstream& ss) const {
+ ss << "Drops a single role. Before deleting the role completely it must remove it "
+ "from any users or roles that reference it. If any errors occur in the middle "
+ "of that process it's possible to be left in a state where the role has been "
+ "removed from some user/roles but otherwise still exists.";
+ }
+
+ virtual Status checkAuthForCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
+ return auth::checkAuthForDropRoleCommand(client, dbname, cmdObj);
+ }
+
+ bool run(OperationContext* txn,
+ const string& dbname,
+ BSONObj& cmdObj,
+ int options,
+ string& errmsg,
+ BSONObjBuilder& result) {
+ const bool ok = grid.catalogManager()->runUserManagementWriteCommand(this->name,
+ dbname,
+ cmdObj,
+ &result);
+
+ AuthorizationManager* authzManager = getGlobalAuthorizationManager();
+ invariant(authzManager);
+ authzManager->invalidateUserCache();
+
+ return ok;
+ }
+
+ } cmdDropRole;
+
+ class CmdDropAllRolesFromDatabase: public Command {
+ public:
+
+ CmdDropAllRolesFromDatabase() : Command("dropAllRolesFromDatabase") {}
+
+ virtual bool slaveOk() const { return false; }
+
+ virtual bool isWriteCommandForConfigServer() const { return false; }
+
+ virtual void help(stringstream& ss) const {
+ ss << "Drops all roles from the given database. Before deleting the roles completely "
+ "it must remove them from any users or other roles that reference them. If any "
+ "errors occur in the middle of that process it's possible to be left in a state "
+ "where the roles have been removed from some user/roles but otherwise still "
+ "exist.";
+ }
+
+ virtual Status checkAuthForCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
+ return auth::checkAuthForDropAllRolesFromDatabaseCommand(client, dbname);
+ }
+
+ bool run(OperationContext* txn,
+ const string& dbname,
+ BSONObj& cmdObj,
+ int options,
+ string& errmsg,
+ BSONObjBuilder& result) {
+ const bool ok = grid.catalogManager()->runUserManagementWriteCommand(this->name,
+ dbname,
+ cmdObj,
+ &result);
+
+ AuthorizationManager* authzManager = getGlobalAuthorizationManager();
+ invariant(authzManager);
+ authzManager->invalidateUserCache();
+
+ return ok;
+ }
+
+ } cmdDropAllRolesFromDatabase;
+
+ class CmdRolesInfo: public Command {
+ public:
+
+ CmdRolesInfo() : Command("rolesInfo") {}
+
+ virtual bool slaveOk() const { return false; }
+
+ virtual bool slaveOverrideOk() const { return true; }
+
+ virtual bool isWriteCommandForConfigServer() const { return false; }
+
+ virtual void help(stringstream& ss) const {
+ ss << "Returns information about roles.";
+ }
+
+ virtual Status checkAuthForCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
+ return auth::checkAuthForRolesInfoCommand(client, dbname, cmdObj);
+ }
+
+ bool run(OperationContext* txn,
+ const string& dbname,
+ BSONObj& cmdObj,
+ int options,
+ string& errmsg,
+ BSONObjBuilder& result) {
+ return grid.catalogManager()->runUserManagementReadCommand(dbname,
+ cmdObj,
+ &result);
+ }
+
+ } cmdRolesInfo;
+
+ class CmdInvalidateUserCache: public Command {
+ public:
+
+ CmdInvalidateUserCache() : Command("invalidateUserCache") {}
+
+ virtual bool slaveOk() const { return true; }
+
+ virtual bool adminOnly() const { return true; }
+
+ virtual bool isWriteCommandForConfigServer() const { return false; }
+
+ virtual void help(stringstream& ss) const {
+ ss << "Invalidates the in-memory cache of user information";
+ }
+
+ virtual Status checkAuthForCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
+ return auth::checkAuthForInvalidateUserCacheCommand(client);
+ }
+
+ bool run(OperationContext* txn,
+ const string& dbname,
+ BSONObj& cmdObj,
+ int options,
+ string& errmsg,
+ BSONObjBuilder& result) {
+ AuthorizationManager* authzManager = getGlobalAuthorizationManager();
+ invariant(authzManager);
+ authzManager->invalidateUserCache();
+ return true;
+ }
+
+ } cmdInvalidateUserCache;
+
+ /**
+ * This command is used only by mongorestore to handle restoring users/roles. We do this so
+ * that mongorestore doesn't do direct inserts into the admin.system.users and
+ * admin.system.roles, which would bypass the authzUpdateLock and allow multiple concurrent
+ * modifications to users/roles. What mongorestore now does instead is it inserts all user/role
+ * definitions it wants to restore into temporary collections, then this command moves those
+ * user/role definitions into their proper place in admin.system.users and admin.system.roles.
+ * It either adds the users/roles to the existing ones or replaces the existing ones, depending
+ * on whether the "drop" argument is true or false.
+ */
+ class CmdMergeAuthzCollections : public Command {
+ public:
+
+ CmdMergeAuthzCollections() : Command("_mergeAuthzCollections") {}
+
+ virtual bool slaveOk() const { return false; }
+
+ virtual bool isWriteCommandForConfigServer() const { return false; }
+
+ virtual bool adminOnly() const { return true; }
+
+ virtual void help(stringstream& ss) const {
+ ss << "Internal command used by mongorestore for updating user/role data";
+ }
+
+ virtual Status checkAuthForCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
+ return auth::checkAuthForMergeAuthzCollectionsCommand(client, cmdObj);
+ }
+
+ bool run(OperationContext* txn,
+ const string& dbname,
+ BSONObj& cmdObj,
+ int options,
+ string& errmsg,
+ BSONObjBuilder& result) {
+ return grid.catalogManager()->runUserManagementWriteCommand(this->name,
+ dbname,
+ cmdObj,
+ &result);
+ }
+
+ } cmdMergeAuthzCollections;
+
+} // namespace mongo