StatusWith<UserHandle> AuthorizationManagerImpl::_acquireUserSlowPath(CacheGuard& guard, OperationContext* opCtx, const UserName& userName) { int authzVersion = _version; // Number of times to retry a user document that fetches due to transient // AuthSchemaIncompatible errors. These errors should only ever occur during and shortly // after schema upgrades. static const int maxAcquireRetries = 2; Status status = Status::OK(); std::unique_ptr<User> user; for (int i = 0; i < maxAcquireRetries; ++i) { if (authzVersion == schemaVersionInvalid) { Status status = _externalState->getStoredAuthorizationVersion(opCtx, &authzVersion); if (!status.isOK()) return status; } switch (authzVersion) { default: status = Status(ErrorCodes::BadValue, mongoutils::str::stream() << "Illegal value for authorization data schema version, " << authzVersion); break; case schemaVersion28SCRAM: case schemaVersion26Final: case schemaVersion26Upgrade: status = _fetchUserV2(opCtx, userName, &user); break; case schemaVersion24: status = Status(ErrorCodes::AuthSchemaIncompatible, mongoutils::str::stream() << "Authorization data schema version " << schemaVersion24 << " not supported after MongoDB version 2.6."); break; } if (status.isOK()) break; if (status != ErrorCodes::AuthSchemaIncompatible) return status; authzVersion = schemaVersionInvalid; } if (!status.isOK()) return status; // All this does is re-acquire the _cacheWriteMutex if we don't hold it already - a caller // may also call endFetchPhase() after this returns. guard.endFetchPhase(); if (guard.isSameCacheGeneration()) { if (_version == schemaVersionInvalid) _version = authzVersion; return _userCache.insertOrAssignAndGet(userName, std::move(user)); } else { // If the cache generation changed while this thread was in fetch mode, the data // associated with the user may now be invalid, so we must mark it as such. The caller // may still opt to use the information for a short while, but not indefinitely. user->_invalidate(); return UserHandle(std::move(user)); } }
Status AuthorizationManager::acquireUser( OperationContext* txn, const UserName& userName, User** acquiredUser) { if (userName == internalSecurity.user->getName()) { *acquiredUser = internalSecurity.user; return Status::OK(); } unordered_map<UserName, User*>::iterator it; CacheGuard guard(this, CacheGuard::fetchSynchronizationManual); while ((_userCache.end() == (it = _userCache.find(userName))) && guard.otherUpdateInFetchPhase()) { guard.wait(); } if (it != _userCache.end()) { fassert(16914, it->second); fassert(17003, it->second->isValid()); fassert(17008, it->second->getRefCount() > 0); it->second->incrementRefCount(); *acquiredUser = it->second; return Status::OK(); } std::auto_ptr<User> user; int authzVersion = _version; guard.beginFetchPhase(); // Number of times to retry a user document that fetches due to transient // AuthSchemaIncompatible errors. These errors should only ever occur during and shortly // after schema upgrades. static const int maxAcquireRetries = 2; Status status = Status::OK(); for (int i = 0; i < maxAcquireRetries; ++i) { if (authzVersion == schemaVersionInvalid) { Status status = _externalState->getStoredAuthorizationVersion(txn, &authzVersion); if (!status.isOK()) return status; } switch (authzVersion) { default: status = Status(ErrorCodes::BadValue, mongoutils::str::stream() << "Illegal value for authorization data schema version, " << authzVersion); break; case schemaVersion26Final: case schemaVersion26Upgrade: status = _fetchUserV2(txn, userName, &user); break; case schemaVersion24: status = Status(ErrorCodes::AuthSchemaIncompatible, mongoutils::str::stream() << "Authorization data schema version " << schemaVersion24 << " not supported after MongoDB version 2.6."); break; } if (status.isOK()) break; if (status != ErrorCodes::AuthSchemaIncompatible) return status; authzVersion = schemaVersionInvalid; } if (!status.isOK()) return status; guard.endFetchPhase(); user->incrementRefCount(); // NOTE: It is not safe to throw an exception from here to the end of the method. if (guard.isSameCacheGeneration()) { _userCache.insert(make_pair(userName, user.get())); if (_version == schemaVersionInvalid) _version = authzVersion; } else { // If the cache generation changed while this thread was in fetch mode, the data // associated with the user may now be invalid, so we must mark it as such. The caller // may still opt to use the information for a short while, but not indefinitely. user->invalidate(); } *acquiredUser = user.release(); return Status::OK(); }