PageRenderTime 46ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 1ms

/src/mongo/db/auth/authorization_manager.cpp

https://github.com/Dazzed/mongo
C++ | 870 lines | 663 code | 100 blank | 107 comment | 127 complexity | 20aa0c195fe637a7452649c18b222db0 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. /**
  2. * Copyright (C) 2012 10gen Inc.
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU Affero General Public License, version 3,
  6. * as published by the Free Software Foundation.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU Affero General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU Affero General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. *
  16. * As a special exception, the copyright holders give permission to link the
  17. * code of portions of this program with the OpenSSL library under certain
  18. * conditions as described in each individual source file and distribute
  19. * linked combinations including the program with the OpenSSL library. You
  20. * must comply with the GNU Affero General Public License in all respects for
  21. * all of the code used other than as permitted herein. If you modify file(s)
  22. * with this exception, you may extend this exception to your version of the
  23. * file(s), but you are not obligated to do so. If you do not wish to do so,
  24. * delete this exception statement from your version. If you delete this
  25. * exception statement from all source files in the program, then also delete
  26. * it in the license file.
  27. */
  28. #include "mongo/platform/basic.h"
  29. #include "mongo/db/auth/authorization_manager.h"
  30. #include <boost/thread/mutex.hpp>
  31. #include <memory>
  32. #include <string>
  33. #include <vector>
  34. #include "mongo/base/init.h"
  35. #include "mongo/base/status.h"
  36. #include "mongo/bson/mutable/document.h"
  37. #include "mongo/bson/mutable/element.h"
  38. #include "mongo/bson/util/bson_extract.h"
  39. #include "mongo/client/auth_helpers.h"
  40. #include "mongo/db/auth/action_set.h"
  41. #include "mongo/db/auth/authz_documents_update_guard.h"
  42. #include "mongo/db/auth/authz_manager_external_state.h"
  43. #include "mongo/db/auth/privilege.h"
  44. #include "mongo/db/auth/role_graph.h"
  45. #include "mongo/db/auth/user.h"
  46. #include "mongo/db/auth/user_document_parser.h"
  47. #include "mongo/db/auth/user_name.h"
  48. #include "mongo/db/auth/user_name_hash.h"
  49. #include "mongo/db/jsobj.h"
  50. #include "mongo/platform/compiler.h"
  51. #include "mongo/platform/unordered_map.h"
  52. #include "mongo/util/assert_util.h"
  53. #include "mongo/util/log.h"
  54. #include "mongo/util/map_util.h"
  55. #include "mongo/util/mongoutils/str.h"
  56. namespace mongo {
  57. AuthInfo internalSecurity;
  58. MONGO_INITIALIZER_WITH_PREREQUISITES(SetupInternalSecurityUser, MONGO_NO_PREREQUISITES)(
  59. InitializerContext* context) {
  60. User* user = new User(UserName("__system", "local"));
  61. user->incrementRefCount(); // Pin this user so the ref count never drops below 1.
  62. ActionSet allActions;
  63. allActions.addAllActions();
  64. PrivilegeVector privileges;
  65. RoleGraph::generateUniversalPrivileges(&privileges);
  66. user->addPrivileges(privileges);
  67. internalSecurity.user = user;
  68. return Status::OK();
  69. }
  70. const std::string AuthorizationManager::USER_NAME_FIELD_NAME = "user";
  71. const std::string AuthorizationManager::USER_DB_FIELD_NAME = "db";
  72. const std::string AuthorizationManager::ROLE_NAME_FIELD_NAME = "role";
  73. const std::string AuthorizationManager::ROLE_SOURCE_FIELD_NAME = "db";
  74. const std::string AuthorizationManager::PASSWORD_FIELD_NAME = "pwd";
  75. const std::string AuthorizationManager::V1_USER_NAME_FIELD_NAME = "user";
  76. const std::string AuthorizationManager::V1_USER_SOURCE_FIELD_NAME = "userSource";
  77. const NamespaceString AuthorizationManager::adminCommandNamespace("admin.$cmd");
  78. const NamespaceString AuthorizationManager::rolesCollectionNamespace("admin.system.roles");
  79. const NamespaceString AuthorizationManager::usersAltCollectionNamespace(
  80. "admin.system.new_users");
  81. const NamespaceString AuthorizationManager::usersBackupCollectionNamespace(
  82. "admin.system.backup_users");
  83. const NamespaceString AuthorizationManager::usersCollectionNamespace("admin.system.users");
  84. const NamespaceString AuthorizationManager::versionCollectionNamespace("admin.system.version");
  85. const NamespaceString AuthorizationManager::defaultTempUsersCollectionNamespace(
  86. "admin.tempusers");
  87. const NamespaceString AuthorizationManager::defaultTempRolesCollectionNamespace(
  88. "admin.temproles");
  89. const BSONObj AuthorizationManager::versionDocumentQuery = BSON("_id" << "authSchema");
  90. const std::string AuthorizationManager::schemaVersionFieldName = "currentVersion";
  91. #ifndef _MSC_EXTENSIONS
  92. const int AuthorizationManager::schemaVersion24;
  93. const int AuthorizationManager::schemaVersion26Upgrade;
  94. const int AuthorizationManager::schemaVersion26Final;
  95. #endif
  96. /**
  97. * Guard object for synchronizing accesses to data cached in AuthorizationManager instances.
  98. * This guard allows one thread to access the cache at a time, and provides an exception-safe
  99. * mechanism for a thread to release the cache mutex while performing network or disk operations
  100. * while allowing other readers to proceed.
  101. *
  102. * There are two ways to use this guard. One may simply instantiate the guard like a
  103. * std::lock_guard, and perform reads or writes of the cache.
  104. *
  105. * Alternatively, one may instantiate the guard, examine the cache, and then enter into an
  106. * update mode by first wait()ing until otherUpdateInFetchPhase() is false, and then
  107. * calling beginFetchPhase(). At this point, other threads may acquire the guard in the simple
  108. * manner and do reads, but other threads may not enter into a fetch phase. During the fetch
  109. * phase, the thread should perform required network or disk activity to determine what update
  110. * it will make to the cache. Then, it should call endFetchPhase(), to reacquire the user cache
  111. * mutex. At that point, the thread can make its modifications to the cache and let the guard
  112. * go out of scope.
  113. *
  114. * All updates by guards using a fetch-phase are totally ordered with respect to one another,
  115. * and all guards using no fetch phase are totally ordered with respect to one another, but
  116. * there is not a total ordering among all guard objects.
  117. *
  118. * The cached data has an associated counter, called the cache generation. If the cache
  119. * generation changes while a guard is in fetch phase, the fetched data should not be stored
  120. * into the cache, because some invalidation event occurred during the fetch phase.
  121. *
  122. * NOTE: It is not safe to enter fetch phase while holding a database lock. Fetch phase
  123. * operations are allowed to acquire database locks themselves, so entering fetch while holding
  124. * a database lock may lead to deadlock.
  125. */
  126. class AuthorizationManager::CacheGuard {
  127. MONGO_DISALLOW_COPYING(CacheGuard);
  128. public:
  129. enum FetchSynchronization {
  130. fetchSynchronizationAutomatic,
  131. fetchSynchronizationManual
  132. };
  133. /**
  134. * Constructs a cache guard, locking the mutex that synchronizes user cache accesses.
  135. */
  136. CacheGuard(AuthorizationManager* authzManager,
  137. const FetchSynchronization sync = fetchSynchronizationAutomatic) :
  138. _isThisGuardInFetchPhase(false),
  139. _authzManager(authzManager),
  140. _lock(authzManager->_cacheMutex) {
  141. if (fetchSynchronizationAutomatic == sync) {
  142. synchronizeWithFetchPhase();
  143. }
  144. }
  145. /**
  146. * Releases the mutex that synchronizes user cache access, if held, and notifies
  147. * any threads waiting for their own opportunity to update the user cache.
  148. */
  149. ~CacheGuard() {
  150. if (!_lock.owns_lock()) {
  151. _lock.lock();
  152. }
  153. if (_isThisGuardInFetchPhase) {
  154. fassert(17190, _authzManager->_isFetchPhaseBusy);
  155. _authzManager->_isFetchPhaseBusy = false;
  156. _authzManager->_fetchPhaseIsReady.notify_all();
  157. }
  158. }
  159. /**
  160. * Returns true of the authzManager reports that it is in fetch phase.
  161. */
  162. bool otherUpdateInFetchPhase() { return _authzManager->_isFetchPhaseBusy; }
  163. /**
  164. * Waits on the _authzManager->_fetchPhaseIsReady condition.
  165. */
  166. void wait() {
  167. fassert(17222, !_isThisGuardInFetchPhase);
  168. _authzManager->_fetchPhaseIsReady.wait(_lock);
  169. }
  170. /**
  171. * Enters fetch phase, releasing the _authzManager->_cacheMutex after recording the current
  172. * cache generation.
  173. */
  174. void beginFetchPhase() {
  175. fassert(17191, !_authzManager->_isFetchPhaseBusy);
  176. _isThisGuardInFetchPhase = true;
  177. _authzManager->_isFetchPhaseBusy = true;
  178. _startGeneration = _authzManager->_cacheGeneration;
  179. _lock.unlock();
  180. }
  181. /**
  182. * Exits the fetch phase, reacquiring the _authzManager->_cacheMutex.
  183. */
  184. void endFetchPhase() {
  185. _lock.lock();
  186. // We do not clear _authzManager->_isFetchPhaseBusy or notify waiters until
  187. // ~CacheGuard(), for two reasons. First, there's no value to notifying the waiters
  188. // before you're ready to release the mutex, because they'll just go to sleep on the
  189. // mutex. Second, in order to meaningfully check the preconditions of
  190. // isSameCacheGeneration(), we need a state that means "fetch phase was entered and now
  191. // has been exited." That state is _isThisGuardInFetchPhase == true and
  192. // _lock.owns_lock() == true.
  193. }
  194. /**
  195. * Returns true if _authzManager->_cacheGeneration remained the same while this guard was
  196. * in fetch phase. Behavior is undefined if this guard never entered fetch phase.
  197. *
  198. * If this returns true, do not update the cached data with this
  199. */
  200. bool isSameCacheGeneration() const {
  201. fassert(17223, _isThisGuardInFetchPhase);
  202. fassert(17231, _lock.owns_lock());
  203. return _startGeneration == _authzManager->_cacheGeneration;
  204. }
  205. private:
  206. void synchronizeWithFetchPhase() {
  207. while (otherUpdateInFetchPhase())
  208. wait();
  209. fassert(17192, !_authzManager->_isFetchPhaseBusy);
  210. _isThisGuardInFetchPhase = true;
  211. _authzManager->_isFetchPhaseBusy = true;
  212. }
  213. OID _startGeneration;
  214. bool _isThisGuardInFetchPhase;
  215. AuthorizationManager* _authzManager;
  216. boost::unique_lock<boost::mutex> _lock;
  217. };
  218. AuthorizationManager::AuthorizationManager(AuthzManagerExternalState* externalState) :
  219. _authEnabled(false),
  220. _externalState(externalState),
  221. _version(schemaVersionInvalid),
  222. _isFetchPhaseBusy(false) {
  223. _updateCacheGeneration_inlock();
  224. }
  225. AuthorizationManager::~AuthorizationManager() {
  226. for (unordered_map<UserName, User*>::iterator it = _userCache.begin();
  227. it != _userCache.end(); ++it) {
  228. fassert(17265, it->second != internalSecurity.user);
  229. delete it->second ;
  230. }
  231. }
  232. Status AuthorizationManager::getAuthorizationVersion(OperationContext* txn, int* version) {
  233. CacheGuard guard(this, CacheGuard::fetchSynchronizationManual);
  234. int newVersion = _version;
  235. if (schemaVersionInvalid == newVersion) {
  236. while (guard.otherUpdateInFetchPhase())
  237. guard.wait();
  238. guard.beginFetchPhase();
  239. Status status = _externalState->getStoredAuthorizationVersion(txn, &newVersion);
  240. guard.endFetchPhase();
  241. if (!status.isOK()) {
  242. warning() << "Problem fetching the stored schema version of authorization data: "
  243. << status;
  244. *version = schemaVersionInvalid;
  245. return status;
  246. }
  247. if (guard.isSameCacheGeneration()) {
  248. _version = newVersion;
  249. }
  250. }
  251. *version = newVersion;
  252. return Status::OK();
  253. }
  254. OID AuthorizationManager::getCacheGeneration() {
  255. CacheGuard guard(this, CacheGuard::fetchSynchronizationManual);
  256. return _cacheGeneration;
  257. }
  258. void AuthorizationManager::setAuthEnabled(bool enabled) {
  259. _authEnabled = enabled;
  260. }
  261. bool AuthorizationManager::isAuthEnabled() const {
  262. return _authEnabled;
  263. }
  264. bool AuthorizationManager::hasAnyPrivilegeDocuments(OperationContext* txn) const {
  265. return _externalState->hasAnyPrivilegeDocuments(txn);
  266. }
  267. Status AuthorizationManager::writeAuthSchemaVersionIfNeeded() {
  268. Status status = _externalState->updateOne(
  269. AuthorizationManager::versionCollectionNamespace,
  270. AuthorizationManager::versionDocumentQuery,
  271. BSON("$set" << BSON(AuthorizationManager::schemaVersionFieldName <<
  272. AuthorizationManager::schemaVersion26Final)),
  273. true, // upsert
  274. BSONObj()); // write concern
  275. if (status == ErrorCodes::NoMatchingDocument) { // SERVER-11492
  276. status = Status::OK();
  277. }
  278. return status;
  279. }
  280. Status AuthorizationManager::insertPrivilegeDocument(const std::string& dbname,
  281. const BSONObj& userObj,
  282. const BSONObj& writeConcern) const {
  283. return _externalState->insertPrivilegeDocument(dbname, userObj, writeConcern);
  284. }
  285. Status AuthorizationManager::updatePrivilegeDocument(const UserName& user,
  286. const BSONObj& updateObj,
  287. const BSONObj& writeConcern) const {
  288. return _externalState->updatePrivilegeDocument(user, updateObj, writeConcern);
  289. }
  290. Status AuthorizationManager::removePrivilegeDocuments(const BSONObj& query,
  291. const BSONObj& writeConcern,
  292. int* numRemoved) const {
  293. return _externalState->removePrivilegeDocuments(query, writeConcern, numRemoved);
  294. }
  295. Status AuthorizationManager::removeRoleDocuments(const BSONObj& query,
  296. const BSONObj& writeConcern,
  297. int* numRemoved) const {
  298. Status status = _externalState->remove(rolesCollectionNamespace,
  299. query,
  300. writeConcern,
  301. numRemoved);
  302. if (status.code() == ErrorCodes::UnknownError) {
  303. return Status(ErrorCodes::RoleModificationFailed, status.reason());
  304. }
  305. return status;
  306. }
  307. Status AuthorizationManager::insertRoleDocument(const BSONObj& roleObj,
  308. const BSONObj& writeConcern) const {
  309. Status status = _externalState->insert(rolesCollectionNamespace,
  310. roleObj,
  311. writeConcern);
  312. if (status.isOK()) {
  313. return status;
  314. }
  315. if (status.code() == ErrorCodes::DuplicateKey) {
  316. std::string name = roleObj[AuthorizationManager::ROLE_NAME_FIELD_NAME].String();
  317. std::string source = roleObj[AuthorizationManager::ROLE_SOURCE_FIELD_NAME].String();
  318. return Status(ErrorCodes::DuplicateKey,
  319. mongoutils::str::stream() << "Role \"" << name << "@" << source <<
  320. "\" already exists");
  321. }
  322. if (status.code() == ErrorCodes::UnknownError) {
  323. return Status(ErrorCodes::RoleModificationFailed, status.reason());
  324. }
  325. return status;
  326. }
  327. Status AuthorizationManager::updateRoleDocument(const RoleName& role,
  328. const BSONObj& updateObj,
  329. const BSONObj& writeConcern) const {
  330. Status status = _externalState->updateOne(
  331. rolesCollectionNamespace,
  332. BSON(AuthorizationManager::ROLE_NAME_FIELD_NAME << role.getRole() <<
  333. AuthorizationManager::ROLE_SOURCE_FIELD_NAME << role.getDB()),
  334. updateObj,
  335. false,
  336. writeConcern);
  337. if (status.isOK()) {
  338. return status;
  339. }
  340. if (status.code() == ErrorCodes::NoMatchingDocument) {
  341. return Status(ErrorCodes::RoleNotFound,
  342. mongoutils::str::stream() << "Role " << role.getFullName() <<
  343. " not found");
  344. }
  345. if (status.code() == ErrorCodes::UnknownError) {
  346. return Status(ErrorCodes::RoleModificationFailed, status.reason());
  347. }
  348. return status;
  349. }
  350. Status AuthorizationManager::queryAuthzDocument(
  351. const NamespaceString& collectionName,
  352. const BSONObj& query,
  353. const BSONObj& projection,
  354. const stdx::function<void(const BSONObj&)>& resultProcessor) {
  355. return _externalState->query(collectionName, query, projection, resultProcessor);
  356. }
  357. Status AuthorizationManager::updateAuthzDocuments(const NamespaceString& collectionName,
  358. const BSONObj& query,
  359. const BSONObj& updatePattern,
  360. bool upsert,
  361. bool multi,
  362. const BSONObj& writeConcern,
  363. int* nMatched) const {
  364. return _externalState->update(collectionName,
  365. query,
  366. updatePattern,
  367. upsert,
  368. multi,
  369. writeConcern,
  370. nMatched);
  371. }
  372. Status AuthorizationManager::getBSONForPrivileges(const PrivilegeVector& privileges,
  373. mutablebson::Element resultArray) {
  374. for (PrivilegeVector::const_iterator it = privileges.begin();
  375. it != privileges.end(); ++it) {
  376. std::string errmsg;
  377. ParsedPrivilege privilege;
  378. if (!ParsedPrivilege::privilegeToParsedPrivilege(*it, &privilege, &errmsg)) {
  379. return Status(ErrorCodes::BadValue, errmsg);
  380. }
  381. resultArray.appendObject("privileges", privilege.toBSON());
  382. }
  383. return Status::OK();
  384. }
  385. Status AuthorizationManager::getBSONForRole(RoleGraph* graph,
  386. const RoleName& roleName,
  387. mutablebson::Element result) {
  388. if (!graph->roleExists(roleName)) {
  389. return Status(ErrorCodes::RoleNotFound,
  390. mongoutils::str::stream() << roleName.getFullName() <<
  391. "does not name an existing role");
  392. }
  393. std::string id = mongoutils::str::stream() << roleName.getDB() << "." << roleName.getRole();
  394. result.appendString("_id", id);
  395. result.appendString(ROLE_NAME_FIELD_NAME, roleName.getRole());
  396. result.appendString(ROLE_SOURCE_FIELD_NAME, roleName.getDB());
  397. // Build privileges array
  398. mutablebson::Element privilegesArrayElement =
  399. result.getDocument().makeElementArray("privileges");
  400. result.pushBack(privilegesArrayElement);
  401. const PrivilegeVector& privileges = graph->getDirectPrivileges(roleName);
  402. Status status = getBSONForPrivileges(privileges, privilegesArrayElement);
  403. if (!status.isOK()) {
  404. return status;
  405. }
  406. // Build roles array
  407. mutablebson::Element rolesArrayElement = result.getDocument().makeElementArray("roles");
  408. result.pushBack(rolesArrayElement);
  409. for (RoleNameIterator roles = graph->getDirectSubordinates(roleName);
  410. roles.more();
  411. roles.next()) {
  412. const RoleName& subRole = roles.get();
  413. mutablebson::Element roleObj = result.getDocument().makeElementObject("");
  414. roleObj.appendString(ROLE_NAME_FIELD_NAME, subRole.getRole());
  415. roleObj.appendString(ROLE_SOURCE_FIELD_NAME, subRole.getDB());
  416. rolesArrayElement.pushBack(roleObj);
  417. }
  418. return Status::OK();
  419. }
  420. Status AuthorizationManager::_initializeUserFromPrivilegeDocument(
  421. User* user, const BSONObj& privDoc) {
  422. V2UserDocumentParser parser;
  423. std::string userName = parser.extractUserNameFromUserDocument(privDoc);
  424. if (userName != user->getName().getUser()) {
  425. return Status(ErrorCodes::BadValue,
  426. mongoutils::str::stream() << "User name from privilege document \""
  427. << userName
  428. << "\" doesn't match name of provided User \""
  429. << user->getName().getUser()
  430. << "\"",
  431. 0);
  432. }
  433. Status status = parser.initializeUserCredentialsFromUserDocument(user, privDoc);
  434. if (!status.isOK()) {
  435. return status;
  436. }
  437. status = parser.initializeUserRolesFromUserDocument(privDoc, user);
  438. if (!status.isOK()) {
  439. return status;
  440. }
  441. status = parser.initializeUserPrivilegesFromUserDocument(privDoc, user);
  442. return Status::OK();
  443. }
  444. Status AuthorizationManager::getUserDescription(OperationContext* txn,
  445. const UserName& userName,
  446. BSONObj* result) {
  447. return _externalState->getUserDescription(txn, userName, result);
  448. }
  449. Status AuthorizationManager::getRoleDescription(const RoleName& roleName,
  450. bool showPrivileges,
  451. BSONObj* result) {
  452. return _externalState->getRoleDescription(roleName, showPrivileges, result);
  453. }
  454. Status AuthorizationManager::getRoleDescriptionsForDB(const std::string dbname,
  455. bool showPrivileges,
  456. bool showBuiltinRoles,
  457. vector<BSONObj>* result) {
  458. return _externalState->getRoleDescriptionsForDB(dbname,
  459. showPrivileges,
  460. showBuiltinRoles,
  461. result);
  462. }
  463. Status AuthorizationManager::acquireUser(
  464. OperationContext* txn, const UserName& userName, User** acquiredUser) {
  465. if (userName == internalSecurity.user->getName()) {
  466. *acquiredUser = internalSecurity.user;
  467. return Status::OK();
  468. }
  469. unordered_map<UserName, User*>::iterator it;
  470. CacheGuard guard(this, CacheGuard::fetchSynchronizationManual);
  471. while ((_userCache.end() == (it = _userCache.find(userName))) &&
  472. guard.otherUpdateInFetchPhase()) {
  473. guard.wait();
  474. }
  475. if (it != _userCache.end()) {
  476. fassert(16914, it->second);
  477. fassert(17003, it->second->isValid());
  478. fassert(17008, it->second->getRefCount() > 0);
  479. it->second->incrementRefCount();
  480. *acquiredUser = it->second;
  481. return Status::OK();
  482. }
  483. std::auto_ptr<User> user;
  484. int authzVersion = _version;
  485. guard.beginFetchPhase();
  486. // Number of times to retry a user document that fetches due to transient
  487. // AuthSchemaIncompatible errors. These errors should only ever occur during and shortly
  488. // after schema upgrades.
  489. static const int maxAcquireRetries = 2;
  490. Status status = Status::OK();
  491. for (int i = 0; i < maxAcquireRetries; ++i) {
  492. if (authzVersion == schemaVersionInvalid) {
  493. Status status = _externalState->getStoredAuthorizationVersion(txn, &authzVersion);
  494. if (!status.isOK())
  495. return status;
  496. }
  497. switch (authzVersion) {
  498. default:
  499. status = Status(ErrorCodes::BadValue, mongoutils::str::stream() <<
  500. "Illegal value for authorization data schema version, " <<
  501. authzVersion);
  502. break;
  503. case schemaVersion26Final:
  504. case schemaVersion26Upgrade:
  505. status = _fetchUserV2(txn, userName, &user);
  506. break;
  507. case schemaVersion24:
  508. status = Status(ErrorCodes::AuthSchemaIncompatible, mongoutils::str::stream() <<
  509. "Authorization data schema version " << schemaVersion24 <<
  510. " not supported after MongoDB version 2.6.");
  511. break;
  512. }
  513. if (status.isOK())
  514. break;
  515. if (status != ErrorCodes::AuthSchemaIncompatible)
  516. return status;
  517. authzVersion = schemaVersionInvalid;
  518. }
  519. if (!status.isOK())
  520. return status;
  521. guard.endFetchPhase();
  522. user->incrementRefCount();
  523. // NOTE: It is not safe to throw an exception from here to the end of the method.
  524. if (guard.isSameCacheGeneration()) {
  525. _userCache.insert(make_pair(userName, user.get()));
  526. if (_version == schemaVersionInvalid)
  527. _version = authzVersion;
  528. }
  529. else {
  530. // If the cache generation changed while this thread was in fetch mode, the data
  531. // associated with the user may now be invalid, so we must mark it as such. The caller
  532. // may still opt to use the information for a short while, but not indefinitely.
  533. user->invalidate();
  534. }
  535. *acquiredUser = user.release();
  536. return Status::OK();
  537. }
  538. Status AuthorizationManager::_fetchUserV2(OperationContext* txn,
  539. const UserName& userName,
  540. std::auto_ptr<User>* acquiredUser) {
  541. BSONObj userObj;
  542. Status status = getUserDescription(txn, userName, &userObj);
  543. if (!status.isOK()) {
  544. return status;
  545. }
  546. // Put the new user into an auto_ptr temporarily in case there's an error while
  547. // initializing the user.
  548. std::auto_ptr<User> user(new User(userName));
  549. status = _initializeUserFromPrivilegeDocument(user.get(), userObj);
  550. if (!status.isOK()) {
  551. return status;
  552. }
  553. acquiredUser->reset(user.release());
  554. return Status::OK();
  555. }
  556. void AuthorizationManager::releaseUser(User* user) {
  557. if (user == internalSecurity.user) {
  558. return;
  559. }
  560. CacheGuard guard(this, CacheGuard::fetchSynchronizationManual);
  561. user->decrementRefCount();
  562. if (user->getRefCount() == 0) {
  563. // If it's been invalidated then it's not in the _userCache anymore.
  564. if (user->isValid()) {
  565. MONGO_COMPILER_VARIABLE_UNUSED bool erased = _userCache.erase(user->getName());
  566. dassert(erased);
  567. }
  568. delete user;
  569. }
  570. }
  571. void AuthorizationManager::invalidateUserByName(const UserName& userName) {
  572. CacheGuard guard(this, CacheGuard::fetchSynchronizationManual);
  573. _updateCacheGeneration_inlock();
  574. unordered_map<UserName, User*>::iterator it = _userCache.find(userName);
  575. if (it == _userCache.end()) {
  576. return;
  577. }
  578. User* user = it->second;
  579. _userCache.erase(it);
  580. user->invalidate();
  581. }
  582. void AuthorizationManager::invalidateUsersFromDB(const std::string& dbname) {
  583. CacheGuard guard(this, CacheGuard::fetchSynchronizationManual);
  584. _updateCacheGeneration_inlock();
  585. unordered_map<UserName, User*>::iterator it = _userCache.begin();
  586. while (it != _userCache.end()) {
  587. User* user = it->second;
  588. if (user->getName().getDB() == dbname) {
  589. _userCache.erase(it++);
  590. user->invalidate();
  591. } else {
  592. ++it;
  593. }
  594. }
  595. }
  596. void AuthorizationManager::invalidateUserCache() {
  597. CacheGuard guard(this, CacheGuard::fetchSynchronizationManual);
  598. _invalidateUserCache_inlock();
  599. }
  600. void AuthorizationManager::_invalidateUserCache_inlock() {
  601. _updateCacheGeneration_inlock();
  602. for (unordered_map<UserName, User*>::iterator it = _userCache.begin();
  603. it != _userCache.end(); ++it) {
  604. fassert(17266, it->second != internalSecurity.user);
  605. it->second->invalidate();
  606. }
  607. _userCache.clear();
  608. // Reread the schema version before acquiring the next user.
  609. _version = schemaVersionInvalid;
  610. }
  611. Status AuthorizationManager::initialize() {
  612. invalidateUserCache();
  613. Status status = _externalState->initialize();
  614. if (!status.isOK())
  615. return status;
  616. return Status::OK();
  617. }
  618. bool AuthorizationManager::tryAcquireAuthzUpdateLock(const StringData& why) {
  619. return _externalState->tryAcquireAuthzUpdateLock(why);
  620. }
  621. void AuthorizationManager::releaseAuthzUpdateLock() {
  622. return _externalState->releaseAuthzUpdateLock();
  623. }
  624. Status AuthorizationManager::upgradeSchemaStep(
  625. OperationContext* txn, const BSONObj& writeConcern, bool* isDone) {
  626. int authzVersion;
  627. Status status = getAuthorizationVersion(txn, &authzVersion);
  628. if (!status.isOK()) {
  629. return status;
  630. }
  631. switch (authzVersion) {
  632. case schemaVersion26Final:
  633. *isDone = true;
  634. return Status::OK();
  635. default:
  636. return Status(ErrorCodes::AuthSchemaIncompatible, mongoutils::str::stream() <<
  637. "Do not know how to upgrade auth schema from version " << authzVersion);
  638. }
  639. }
  640. Status AuthorizationManager::upgradeSchema(
  641. OperationContext* txn, int maxSteps, const BSONObj& writeConcern) {
  642. if (maxSteps < 1) {
  643. return Status(ErrorCodes::BadValue,
  644. "Minimum value for maxSteps parameter to upgradeSchema is 1");
  645. }
  646. invalidateUserCache();
  647. for (int i = 0; i < maxSteps; ++i) {
  648. bool isDone;
  649. Status status = upgradeSchemaStep(txn, writeConcern, &isDone);
  650. invalidateUserCache();
  651. if (!status.isOK() || isDone) {
  652. return status;
  653. }
  654. }
  655. return Status(ErrorCodes::OperationIncomplete, mongoutils::str::stream() <<
  656. "Auth schema upgrade incomplete after " << maxSteps << " successful steps.");
  657. }
  658. namespace {
  659. bool isAuthzNamespace(const StringData& ns) {
  660. return (ns == AuthorizationManager::rolesCollectionNamespace.ns() ||
  661. ns == AuthorizationManager::usersCollectionNamespace.ns() ||
  662. ns == AuthorizationManager::versionCollectionNamespace.ns());
  663. }
  664. bool isAuthzCollection(const StringData& coll) {
  665. return (coll == AuthorizationManager::rolesCollectionNamespace.coll() ||
  666. coll == AuthorizationManager::usersCollectionNamespace.coll() ||
  667. coll == AuthorizationManager::versionCollectionNamespace.coll());
  668. }
  669. bool loggedCommandOperatesOnAuthzData(const char* ns, const BSONObj& cmdObj) {
  670. if (ns != AuthorizationManager::adminCommandNamespace.ns())
  671. return false;
  672. const StringData cmdName(cmdObj.firstElement().fieldNameStringData());
  673. if (cmdName == "drop") {
  674. return isAuthzCollection(cmdObj.firstElement().valueStringData());
  675. }
  676. else if (cmdName == "dropDatabase") {
  677. return true;
  678. }
  679. else if (cmdName == "renameCollection") {
  680. return isAuthzCollection(cmdObj.firstElement().str()) ||
  681. isAuthzCollection(cmdObj["to"].str());
  682. }
  683. else if (cmdName == "dropIndexes" || cmdName == "deleteIndexes") {
  684. return false;
  685. }
  686. else if (cmdName == "create") {
  687. return false;
  688. }
  689. else {
  690. return true;
  691. }
  692. }
  693. bool appliesToAuthzData(
  694. const char* op,
  695. const char* ns,
  696. const BSONObj& o) {
  697. switch (*op) {
  698. case 'i':
  699. case 'u':
  700. case 'd':
  701. if (op[1] != '\0') return false; // "db" op type
  702. return isAuthzNamespace(ns);
  703. case 'c':
  704. return loggedCommandOperatesOnAuthzData(ns, o);
  705. break;
  706. case 'n':
  707. return false;
  708. default:
  709. return true;
  710. }
  711. }
  712. // Updates to users in the oplog are done by matching on the _id, which will always have the
  713. // form "<dbname>.<username>". This function extracts the UserName from that string.
  714. StatusWith<UserName> extractUserNameFromIdString(const StringData& idstr) {
  715. size_t splitPoint = idstr.find('.');
  716. if (splitPoint == string::npos) {
  717. return StatusWith<UserName>(
  718. ErrorCodes::FailedToParse,
  719. mongoutils::str::stream() << "_id entries for user documents must be of "
  720. "the form <dbname>.<username>. Found: " << idstr);
  721. }
  722. return StatusWith<UserName>(UserName(idstr.substr(splitPoint),
  723. idstr.substr(0, splitPoint)));
  724. }
  725. } // namespace
  726. void AuthorizationManager::_updateCacheGeneration_inlock() {
  727. _cacheGeneration = OID::gen();
  728. }
  729. void AuthorizationManager::_invalidateRelevantCacheData(const char* op,
  730. const char* ns,
  731. const BSONObj& o,
  732. const BSONObj* o2) {
  733. if (ns == AuthorizationManager::rolesCollectionNamespace.ns() ||
  734. ns == AuthorizationManager::versionCollectionNamespace.ns()) {
  735. invalidateUserCache();
  736. return;
  737. }
  738. if (*op == 'i' || *op == 'd' || *op == 'u') {
  739. // If you got into this function isAuthzNamespace() must have returned true, and we've
  740. // already checked that it's not the roles or version collection.
  741. invariant(ns == AuthorizationManager::usersCollectionNamespace.ns());
  742. StatusWith<UserName> userName(Status::OK());
  743. if (*op == 'u') {
  744. userName = extractUserNameFromIdString((*o2)["_id"].str());
  745. } else {
  746. userName = extractUserNameFromIdString(o["_id"].str());
  747. }
  748. if (!userName.isOK()) {
  749. warning() << "Invalidating user cache based on user being updated failed, will "
  750. "invalidate the entire cache instead: " << userName.getStatus() << endl;
  751. invalidateUserCache();
  752. return;
  753. }
  754. invalidateUserByName(userName.getValue());
  755. } else {
  756. invalidateUserCache();
  757. }
  758. }
  759. void AuthorizationManager::logOp(
  760. const char* op,
  761. const char* ns,
  762. const BSONObj& o,
  763. BSONObj* o2,
  764. bool* b) {
  765. _externalState->logOp(op, ns, o, o2, b);
  766. if (appliesToAuthzData(op, ns, o)) {
  767. _invalidateRelevantCacheData(op, ns, o, o2);
  768. }
  769. }
  770. } // namespace mongo