/* * Copyright (C) 2013 10gen 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 . * * 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. */ #include "mongo/db/auth/authz_manager_external_state_mock.h" #include #include "mongo/base/status.h" #include "mongo/bson/mutable/algorithm.h" #include "mongo/bson/mutable/document.h" #include "mongo/bson/mutable/element.h" #include "mongo/db/auth/authorization_manager.h" #include "mongo/db/jsobj.h" #include "mongo/db/matcher/expression_parser.h" #include "mongo/db/namespace_string.h" #include "mongo/db/ops/update_driver.h" #include "mongo/platform/unordered_set.h" #include "mongo/util/map_util.h" #include "mongo/util/mongoutils/str.h" namespace mongo { namespace { void addRoleNameToObjectElement(mutablebson::Element object, const RoleName& role) { fassert(17175, object.appendString(AuthorizationManager::ROLE_NAME_FIELD_NAME, role.getRole())); fassert(17176, object.appendString(AuthorizationManager::ROLE_SOURCE_FIELD_NAME, role.getDB())); } void addRoleNameObjectsToArrayElement(mutablebson::Element array, RoleNameIterator roles) { for (; roles.more(); roles.next()) { mutablebson::Element roleElement = array.getDocument().makeElementObject(""); addRoleNameToObjectElement(roleElement, roles.get()); fassert(17177, array.pushBack(roleElement)); } } void addPrivilegeObjectsOrWarningsToArrayElement(mutablebson::Element privilegesElement, mutablebson::Element warningsElement, const PrivilegeVector& privileges) { std::string errmsg; for (size_t i = 0; i < privileges.size(); ++i) { ParsedPrivilege pp; if (ParsedPrivilege::privilegeToParsedPrivilege(privileges[i], &pp, &errmsg)) { fassert(17178, privilegesElement.appendObject("", pp.toBSON())); } else { fassert(17179, warningsElement.appendString( "", std::string(mongoutils::str::stream() << "Skipped privileges on resource " << privileges[i].getResourcePattern().toString() << ". Reason: " << errmsg))); } } } } // namespace AuthzManagerExternalStateMock::AuthzManagerExternalStateMock() : _authzManager(NULL) {} AuthzManagerExternalStateMock::~AuthzManagerExternalStateMock() {} void AuthzManagerExternalStateMock::setAuthorizationManager( AuthorizationManager* authzManager) { _authzManager = authzManager; } void AuthzManagerExternalStateMock::setAuthzVersion(int version) { uassertStatusOK( updateOne(AuthorizationManager::versionCollectionNamespace, AuthorizationManager::versionDocumentQuery, BSON("$set" << BSON(AuthorizationManager::schemaVersionFieldName << version)), true, BSONObj())); } Status AuthzManagerExternalStateMock::_getUserDocument(const UserName& userName, BSONObj* userDoc) { int authzVersion; Status status = getStoredAuthorizationVersion(&authzVersion); if (!status.isOK()) return status; switch (authzVersion) { case AuthorizationManager::schemaVersion26Upgrade: case AuthorizationManager::schemaVersion26Final: break; default: return Status(ErrorCodes::AuthSchemaIncompatible, mongoutils::str::stream() << "Unsupported schema version for getUserDescription(): " << authzVersion); } status = findOne( (authzVersion == AuthorizationManager::schemaVersion26Final ? AuthorizationManager::usersCollectionNamespace : AuthorizationManager::usersAltCollectionNamespace), BSON(AuthorizationManager::USER_NAME_FIELD_NAME << userName.getUser() << AuthorizationManager::USER_DB_FIELD_NAME << userName.getDB()), userDoc); if (status == ErrorCodes::NoMatchingDocument) { status = Status(ErrorCodes::UserNotFound, mongoutils::str::stream() << "Could not find user " << userName.getFullName()); } return status; } Status AuthzManagerExternalStateMock::getAllDatabaseNames( std::vector* dbnames) { unordered_set dbnameSet; NamespaceDocumentMap::const_iterator it; for (it = _documents.begin(); it != _documents.end(); ++it) { dbnameSet.insert(it->first.db().toString()); } *dbnames = std::vector(dbnameSet.begin(), dbnameSet.end()); return Status::OK(); } Status AuthzManagerExternalStateMock::_findUser( const std::string& usersNamespace, const BSONObj& query, BSONObj* result) { if (!findOne(NamespaceString(usersNamespace), query, result).isOK()) { return Status(ErrorCodes::UserNotFound, "No matching user for query " + query.toString()); } return Status::OK(); } Status AuthzManagerExternalStateMock::findOne( const NamespaceString& collectionName, const BSONObj& query, BSONObj* result) { BSONObjCollection::iterator iter; Status status = _findOneIter(collectionName, query, &iter); if (!status.isOK()) return status; *result = iter->copy(); return Status::OK(); } Status AuthzManagerExternalStateMock::query( const NamespaceString& collectionName, const BSONObj& query, const BSONObj&, const boost::function& resultProcessor) { std::vector iterVector; Status status = _queryVector(collectionName, query, &iterVector); if (!status.isOK()) { return status; } try { for (std::vector::iterator it = iterVector.begin(); it != iterVector.end(); ++it) { resultProcessor(**it); } } catch (const DBException& ex) { status = ex.toStatus(); } return status; } Status AuthzManagerExternalStateMock::insert( const NamespaceString& collectionName, const BSONObj& document, const BSONObj&) { BSONObj toInsert; if (document["_id"].eoo()) { BSONObjBuilder docWithIdBuilder; docWithIdBuilder.append("_id", OID::gen()); docWithIdBuilder.appendElements(document); toInsert = docWithIdBuilder.obj(); } else { toInsert = document.copy(); } _documents[collectionName].push_back(toInsert); if (_authzManager) { _authzManager->logOp( "i", collectionName.ns().c_str(), toInsert, NULL, NULL); } return Status::OK(); } Status AuthzManagerExternalStateMock::updateOne( const NamespaceString& collectionName, const BSONObj& query, const BSONObj& updatePattern, bool upsert, const BSONObj& writeConcern) { namespace mmb = mutablebson; UpdateDriver::Options updateOptions; UpdateDriver driver(updateOptions); Status status = driver.parse(updatePattern); if (!status.isOK()) return status; BSONObjCollection::iterator iter; status = _findOneIter(collectionName, query, &iter); mmb::Document document; if (status.isOK()) { document.reset(*iter, mmb::Document::kInPlaceDisabled); BSONObj logObj; status = driver.update(StringData(), &document, &logObj); if (!status.isOK()) return status; BSONObj newObj = document.getObject().copy(); *iter = newObj; BSONObj idQuery = driver.makeOplogEntryQuery(newObj, false); if (_authzManager) { _authzManager->logOp( "u", collectionName.ns().c_str(), logObj, &idQuery, NULL); } return Status::OK(); } else if (status == ErrorCodes::NoMatchingDocument && upsert) { if (query.hasField("_id")) { document.root().appendElement(query["_id"]); } status = driver.populateDocumentWithQueryFields(query, document); if (!status.isOK()) { return status; } status = driver.update(StringData(), &document); if (!status.isOK()) { return status; } return insert(collectionName, document.getObject(), writeConcern); } else { return status; } } Status AuthzManagerExternalStateMock::update(const NamespaceString& collectionName, const BSONObj& query, const BSONObj& updatePattern, bool upsert, bool multi, const BSONObj& writeConcern, int* nMatched) { return Status(ErrorCodes::InternalError, "AuthzManagerExternalStateMock::update not implemented in mock."); } Status AuthzManagerExternalStateMock::remove( const NamespaceString& collectionName, const BSONObj& query, const BSONObj&, int* numRemoved) { int n = 0; BSONObjCollection::iterator iter; while (_findOneIter(collectionName, query, &iter).isOK()) { BSONObj idQuery = (*iter)["_id"].wrap(); _documents[collectionName].erase(iter); ++n; if (_authzManager) { _authzManager->logOp( "d", collectionName.ns().c_str(), idQuery, NULL, NULL); } } *numRemoved = n; return Status::OK(); } Status AuthzManagerExternalStateMock::createIndex( const NamespaceString& collectionName, const BSONObj& pattern, bool unique, const BSONObj&) { return Status::OK(); } Status AuthzManagerExternalStateMock::dropIndexes( const NamespaceString& collectionName, const BSONObj& writeConcern) { return Status::OK(); } bool AuthzManagerExternalStateMock::tryAcquireAuthzUpdateLock(const StringData&) { return true; } void AuthzManagerExternalStateMock::releaseAuthzUpdateLock() {} std::vector AuthzManagerExternalStateMock::getCollectionContents( const NamespaceString& collectionName) { return mapFindWithDefault(_documents, collectionName, std::vector()); } Status AuthzManagerExternalStateMock::_findOneIter( const NamespaceString& collectionName, const BSONObj& query, BSONObjCollection::iterator* result) { std::vector iterVector; Status status = _queryVector(collectionName, query, &iterVector); if (!status.isOK()) { return status; } if (!iterVector.size()) { return Status(ErrorCodes::NoMatchingDocument, "No matching document"); } *result = iterVector.front(); return Status::OK(); } Status AuthzManagerExternalStateMock::_queryVector( const NamespaceString& collectionName, const BSONObj& query, std::vector* result) { StatusWithMatchExpression parseResult = MatchExpressionParser::parse(query, MatchExpressionParser::WhereCallback()); if (!parseResult.isOK()) { return parseResult.getStatus(); } MatchExpression* matcher = parseResult.getValue(); NamespaceDocumentMap::iterator mapIt = _documents.find(collectionName); if (mapIt == _documents.end()) return Status::OK(); for (BSONObjCollection::iterator vecIt = mapIt->second.begin(); vecIt != mapIt->second.end(); ++vecIt) { if (matcher->matchesBSON(*vecIt)) { result->push_back(vecIt); } } return Status::OK(); } } // namespace mongo