diff options
10 files changed, 164 insertions, 0 deletions
diff --git a/src/mongo/db/auth/authorization_manager.cpp b/src/mongo/db/auth/authorization_manager.cpp index 8ace35e30bc..45401ee5c33 100644 --- a/src/mongo/db/auth/authorization_manager.cpp +++ b/src/mongo/db/auth/authorization_manager.cpp @@ -326,6 +326,11 @@ namespace { return _externalState->updatePrivilegeDocument(user, updateObj); } + Status AuthorizationManager::removePrivilegeDocuments(const std::string& dbname, + const BSONObj& query) const { + return _externalState->removePrivilegeDocuments(dbname, query); + } + ActionSet AuthorizationManager::getAllUserActions() const { ActionSet allActions; allActions.addAllActionsFromSet(readRoleActions); diff --git a/src/mongo/db/auth/authorization_manager.h b/src/mongo/db/auth/authorization_manager.h index c35fd0e9f62..ea9c2446f94 100644 --- a/src/mongo/db/auth/authorization_manager.h +++ b/src/mongo/db/auth/authorization_manager.h @@ -103,6 +103,9 @@ namespace mongo { // Updates the given user object with the given update modifier. Status updatePrivilegeDocument(const UserName& user, const BSONObj& updateObj) const; + // Removes users for the given database matching the given query. + Status removePrivilegeDocuments(const std::string& dbname, const BSONObj& query) const; + // Checks to see if "doc" is a valid privilege document, assuming it is stored in the // "system.users" collection of database "dbname". // diff --git a/src/mongo/db/auth/authz_manager_external_state.h b/src/mongo/db/auth/authz_manager_external_state.h index 74584b867c6..72abb3e5190 100644 --- a/src/mongo/db/auth/authz_manager_external_state.h +++ b/src/mongo/db/auth/authz_manager_external_state.h @@ -58,6 +58,10 @@ namespace mongo { virtual Status updatePrivilegeDocument(const UserName& user, const BSONObj& updateObj) const = 0; + // Removes users for the given database matching the given query. + virtual Status removePrivilegeDocuments(const std::string& dbname, + const BSONObj& query) const = 0; + /** * Puts into the *dbnames vector the name of every database in the cluster. */ diff --git a/src/mongo/db/auth/authz_manager_external_state_d.cpp b/src/mongo/db/auth/authz_manager_external_state_d.cpp index 6a72f3b8fac..b6647ded718 100644 --- a/src/mongo/db/auth/authz_manager_external_state_d.cpp +++ b/src/mongo/db/auth/authz_manager_external_state_d.cpp @@ -105,6 +105,40 @@ namespace { } } + Status AuthzManagerExternalStateMongod::removePrivilegeDocuments(const string& dbname, + const BSONObj& query) const { + try { + string userNS = dbname + ".system.users"; + DBDirectClient client; + { + Client::GodScope gs; + // TODO(spencer): Once we're no longer fully rebuilding the user cache on every + // change to user data we should remove the global lock and uncomment the + // WriteContext below + Lock::GlobalWrite w; + // Client::WriteContext ctx(userNS); + client.remove(userNS, query); + } + + // 30 second timeout for w:majority + BSONObj res = client.getLastErrorDetailed(false, false, -1, 30*1000); + string errstr = client.getLastErrorString(res); + if (!errstr.empty()) { + return Status(ErrorCodes::UserModificationFailed, errstr); + } + + int numUpdated = res["n"].numberInt(); + if (numUpdated == 0) { + return Status(ErrorCodes::UserNotFound, + mongoutils::str::stream() << "No users found on database \"" << dbname + << "\" matching query: " << query.toString()); + } + return Status::OK(); + } catch (const DBException& e) { + return e.toStatus(); + } + } + Status AuthzManagerExternalStateMongod::_findUser(const string& usersNamespace, const BSONObj& query, BSONObj* result) const { diff --git a/src/mongo/db/auth/authz_manager_external_state_d.h b/src/mongo/db/auth/authz_manager_external_state_d.h index 3998d6e7885..e4cac9da17a 100644 --- a/src/mongo/db/auth/authz_manager_external_state_d.h +++ b/src/mongo/db/auth/authz_manager_external_state_d.h @@ -41,6 +41,9 @@ namespace mongo { virtual Status updatePrivilegeDocument(const UserName& user, const BSONObj& updateObj) const; + virtual Status removePrivilegeDocuments(const std::string& dbname, + const BSONObj& query) const; + virtual Status getAllDatabaseNames(std::vector<std::string>* dbnames) const; virtual Status getAllV1PrivilegeDocsForDB(const std::string& dbname, diff --git a/src/mongo/db/auth/authz_manager_external_state_mock.cpp b/src/mongo/db/auth/authz_manager_external_state_mock.cpp index 6dbbfffb48a..000edc6e418 100644 --- a/src/mongo/db/auth/authz_manager_external_state_mock.cpp +++ b/src/mongo/db/auth/authz_manager_external_state_mock.cpp @@ -36,6 +36,11 @@ namespace mongo { return Status::OK(); } + Status AuthzManagerExternalStateMock::removePrivilegeDocuments(const std::string& dbname, + const BSONObj& query) const { + return Status::OK(); + } + Status AuthzManagerExternalStateMock::insertPrivilegeDocument(const std::string& dbname, const BSONObj& userObj) { _userDocuments[dbname].push_back(userObj); diff --git a/src/mongo/db/auth/authz_manager_external_state_mock.h b/src/mongo/db/auth/authz_manager_external_state_mock.h index 894088047b1..7551af9c427 100644 --- a/src/mongo/db/auth/authz_manager_external_state_mock.h +++ b/src/mongo/db/auth/authz_manager_external_state_mock.h @@ -45,6 +45,10 @@ namespace mongo { virtual Status updatePrivilegeDocument(const UserName& user, const BSONObj& updateObj) const; + // no-op for the mock + virtual Status removePrivilegeDocuments(const std::string& dbname, + const BSONObj& query) const; + // Non-const version that puts document into a vector that can be accessed later Status insertPrivilegeDocument(const std::string& dbname, const BSONObj& userObj); diff --git a/src/mongo/db/auth/authz_manager_external_state_s.cpp b/src/mongo/db/auth/authz_manager_external_state_s.cpp index 1d68983fb53..a0b3bd35b61 100644 --- a/src/mongo/db/auth/authz_manager_external_state_s.cpp +++ b/src/mongo/db/auth/authz_manager_external_state_s.cpp @@ -126,6 +126,35 @@ namespace { } } + Status AuthzManagerExternalStateMongos::removePrivilegeDocuments(const string& dbname, + const BSONObj& query) const { + try { + string userNS = dbname + ".system.users"; + scoped_ptr<ScopedDbConnection> conn(getConnectionForUsersCollection(userNS)); + + conn->get()->remove(userNS, query); + + // 30 second timeout for w:majority + BSONObj res = conn->get()->getLastErrorDetailed(false, false, -1, 30*1000); + string err = conn->get()->getLastErrorString(res); + conn->done(); + + if (!err.empty()) { + return Status(ErrorCodes::UserModificationFailed, err); + } + + int numUpdated = res["n"].numberInt(); + if (numUpdated == 0) { + return Status(ErrorCodes::UserNotFound, + mongoutils::str::stream() << "No users found on database \"" << dbname + << "\" matching query: " << query.toString()); + } + return Status::OK(); + } catch (const DBException& e) { + return e.toStatus(); + } + } + Status AuthzManagerExternalStateMongos::getAllDatabaseNames( std::vector<std::string>* dbnames) const { try { diff --git a/src/mongo/db/auth/authz_manager_external_state_s.h b/src/mongo/db/auth/authz_manager_external_state_s.h index 7a80b80d124..4a9f72ded7f 100644 --- a/src/mongo/db/auth/authz_manager_external_state_s.h +++ b/src/mongo/db/auth/authz_manager_external_state_s.h @@ -41,6 +41,9 @@ namespace mongo { virtual Status updatePrivilegeDocument(const UserName& user, const BSONObj& updateObj) const; + virtual Status removePrivilegeDocuments(const std::string& dbname, + const BSONObj& query) const; + virtual Status getAllDatabaseNames(std::vector<std::string>* dbnames) const; virtual Status getAllV1PrivilegeDocsForDB(const std::string& dbname, diff --git a/src/mongo/db/commands/user_management_commands.cpp b/src/mongo/db/commands/user_management_commands.cpp index fe53db02fda..2a40a3a4e71 100644 --- a/src/mongo/db/commands/user_management_commands.cpp +++ b/src/mongo/db/commands/user_management_commands.cpp @@ -429,4 +429,78 @@ namespace mongo { return Status::OK(); } } cmdUpdateUser; + + class CmdRemoveUsers : public Command { + public: + + virtual bool logTheOp() { + return false; + } + + virtual bool slaveOk() const { + return false; + } + + virtual LockType locktype() const { + return NONE; + } + + CmdRemoveUsers() : Command("removeUsers") {} + + virtual void help(stringstream& ss) const { + ss << "By default, removes all users for this database. If given a \"user\"" + " argument, removes only that user."<< endl; + } + + virtual void addRequiredPrivileges(const std::string& dbname, + const BSONObj& cmdObj, + std::vector<Privilege>* out) { + // TODO: update this with the new rules around user creation in 2.6. + ActionSet actions; + actions.addAction(ActionType::userAdmin); + out->push_back(Privilege(dbname, actions)); + } + + // TODO: The bulk of the implementation of this will need to change once we're using the + // new v2 authorization storage format. + bool run(const string& dbname, + BSONObj& cmdObj, + int options, + string& errmsg, + BSONObjBuilder& result, + bool fromRepl) { + std::string user; + + if (cmdObj.hasField("user")) { + Status status = bsonExtractStringField(cmdObj, "user", &user); + if (!status.isOK()) { + addStatus(status, result); + return false; + } + } + + BSONObj query; + if (!user.empty()) { + query = BSON("user" << user); + } + AuthorizationManager* authzManager = getGlobalAuthorizationManager(); + Status status = authzManager->removePrivilegeDocuments(dbname, query); + if (!status.isOK()) { + addStatus(status, result); + return false; + } + + // Rebuild full user cache on every user modification. + // TODO(spencer): Remove this once we update user cache on-demand for each user + // modification. + status = authzManager->initializeAllV1UserData(); + if (!status.isOK()) { + addStatus(status, result); + return false; + } + + return true; + } + + } cmdRemoveUser; } |