diff options
49 files changed, 796 insertions, 1238 deletions
diff --git a/jstests/sharding/session_info_in_oplog.js b/jstests/sharding/session_info_in_oplog.js index 78e10be8f5b..86b4c463f0a 100644 --- a/jstests/sharding/session_info_in_oplog.js +++ b/jstests/sharding/session_info_in_oplog.js @@ -7,6 +7,17 @@ var checkOplog = function(mainConn, priConn) { var lsid = UUID(); + var uid = function() { + var user = mainConn.getDB("admin") + .runCommand({connectionStatus: 1}) + .authInfo.authenticatedUsers[0]; + + if (user) { + return computeSHA256Block(user.user + "@" + user.db); + } else { + return computeSHA256Block(""); + } + }(); //////////////////////////////////////////////////////////////////////// // Test insert command @@ -15,7 +26,7 @@ insert: 'user', documents: [{_id: 10}, {_id: 30}], ordered: false, - lsid: {id: {uuid: lsid}}, + lsid: {id: lsid}, txnNumber: NumberLong(34), }; @@ -26,14 +37,16 @@ var firstDoc = oplog.findOne({ns: 'test.user', 'o._id': 10}); assert(firstDoc != null); assert(firstDoc.lsid != null); - assert.eq(lsid, firstDoc.lsid.id.uuid); + assert.eq(lsid, firstDoc.lsid.id); + assert.eq(uid, firstDoc.lsid.uid); assert.eq(NumberLong(34), firstDoc.txnNumber); assert.eq(0, firstDoc.stmtId); var secondDoc = oplog.findOne({ns: 'test.user', 'o._id': 30}); assert(secondDoc != null); assert(secondDoc.lsid != null); - assert.eq(lsid, secondDoc.lsid.id.uuid); + assert.eq(lsid, secondDoc.lsid.id); + assert.eq(uid, firstDoc.lsid.uid); assert.eq(NumberLong(34), secondDoc.txnNumber); assert.eq(1, secondDoc.stmtId); @@ -48,7 +61,7 @@ {q: {_id: 30}, u: {z: 1}} // replacement ], ordered: false, - lsid: {id: {uuid: lsid}}, + lsid: {id: lsid}, txnNumber: NumberLong(35), }; @@ -57,21 +70,24 @@ firstDoc = oplog.findOne({ns: 'test.user', op: 'u', 'o2._id': 10}); assert(firstDoc != null); assert(firstDoc.lsid != null); - assert.eq(lsid, firstDoc.lsid.id.uuid); + assert.eq(lsid, firstDoc.lsid.id); + assert.eq(uid, firstDoc.lsid.uid); assert.eq(NumberLong(35), firstDoc.txnNumber); assert.eq(0, firstDoc.stmtId); secondDoc = oplog.findOne({ns: 'test.user', op: 'i', 'o._id': 20}); assert(secondDoc != null); assert(secondDoc.lsid != null); - assert.eq(lsid, secondDoc.lsid.id.uuid); + assert.eq(lsid, secondDoc.lsid.id); + assert.eq(uid, firstDoc.lsid.uid); assert.eq(NumberLong(35), secondDoc.txnNumber); assert.eq(1, secondDoc.stmtId); var thirdDoc = oplog.findOne({ns: 'test.user', op: 'u', 'o2._id': 30}); assert(thirdDoc != null); assert(thirdDoc.lsid != null); - assert.eq(lsid, thirdDoc.lsid.id.uuid); + assert.eq(lsid, thirdDoc.lsid.id); + assert.eq(uid, firstDoc.lsid.uid); assert.eq(NumberLong(35), thirdDoc.txnNumber); assert.eq(2, thirdDoc.stmtId); @@ -82,7 +98,7 @@ delete: 'user', deletes: [{q: {_id: 10}, limit: 1}, {q: {_id: 20}, limit: 1}], ordered: false, - lsid: {id: {uuid: lsid}}, + lsid: {id: lsid}, txnNumber: NumberLong(36), }; @@ -91,14 +107,16 @@ firstDoc = oplog.findOne({ns: 'test.user', op: 'd', 'o._id': 10}); assert(firstDoc != null); assert(firstDoc.lsid != null); - assert.eq(lsid, firstDoc.lsid.id.uuid); + assert.eq(lsid, firstDoc.lsid.id); + assert.eq(uid, firstDoc.lsid.uid); assert.eq(NumberLong(36), firstDoc.txnNumber); assert.eq(0, firstDoc.stmtId); secondDoc = oplog.findOne({ns: 'test.user', op: 'd', 'o._id': 20}); assert(secondDoc != null); assert(secondDoc.lsid != null); - assert.eq(lsid, secondDoc.lsid.id.uuid); + assert.eq(lsid, secondDoc.lsid.id); + assert.eq(uid, firstDoc.lsid.uid); assert.eq(NumberLong(36), secondDoc.txnNumber); assert.eq(1, secondDoc.stmtId); }; diff --git a/src/mongo/SConscript b/src/mongo/SConscript index d53612857d7..67283e703b4 100644 --- a/src/mongo/SConscript +++ b/src/mongo/SConscript @@ -411,6 +411,7 @@ if not has_option('noshell') and usemozjs: "shell/shell_utils_launcher.cpp", ], LIBDEPS=[ + 'db/logical_session_id_helpers', 'db/catalog/index_key_validate', 'db/index/external_key_generator', 'db/query/command_request_response', diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript index dea25aa4e74..7a906a19450 100644 --- a/src/mongo/db/SConscript +++ b/src/mongo/db/SConscript @@ -206,6 +206,7 @@ env.CppUnitTest( ], LIBDEPS=[ 'logical_session_id', + 'logical_session_id_helpers', 'service_context', '$BUILD_DIR/mongo/db/auth/authorization_manager_mock_init', '$BUILD_DIR/mongo/db/service_context_noop_init', @@ -873,73 +874,40 @@ env.Library( env.Idlc('logical_session_id.idl')[0], ], LIBDEPS=[ - '$BUILD_DIR/mongo/idl/idl_parser', - '$BUILD_DIR/mongo/util/uuid', - '$BUILD_DIR/mongo/base', - ], -) - -env.CppUnitTest( - target='logical_session_id_test', - source=[ - 'logical_session_id_test.cpp', - ], - LIBDEPS=[ - '$BUILD_DIR/mongo/base', - 'logical_session_id' - ], -) - -env.Library( - target='signed_logical_session_id', - source=[ - 'signed_logical_session_id.cpp', - env.Idlc('signed_logical_session_id.idl')[0], - ], - LIBDEPS=[ '$BUILD_DIR/mongo/base', - '$BUILD_DIR/mongo/crypto/sha1_block', + '$BUILD_DIR/mongo/crypto/sha256_block', + '$BUILD_DIR/mongo/crypto/sha_block_${MONGO_CRYPTO}', '$BUILD_DIR/mongo/idl/idl_parser', '$BUILD_DIR/mongo/util/uuid', - 'logical_session_id' - ], -) - -env.CppUnitTest( - target='signed_logical_session_id_test', - source=[ - 'signed_logical_session_id_test.cpp', - ], - LIBDEPS=[ - '$BUILD_DIR/mongo/base', - 'signed_logical_session_id', ], ) env.Library( - target='logical_session_record', + target='logical_session_id_helpers', source=[ - 'logical_session_record.cpp', - env.Idlc('logical_session_record.idl')[0], + 'logical_session_id_helpers.cpp', ], LIBDEPS=[ - '$BUILD_DIR/mongo/base', - '$BUILD_DIR/mongo/db/auth/user_name', - '$BUILD_DIR/mongo/idl/idl_parser', 'logical_session_id', - 'signed_logical_session_id', + 'logical_session_cache', + '$BUILD_DIR/mongo/db/auth/authcore', ], ) env.CppUnitTest( - target='logical_session_record_test', + target='logical_session_id_test', source=[ - 'logical_session_record_test.cpp', + 'logical_session_id_test.cpp', ], LIBDEPS=[ '$BUILD_DIR/mongo/base', + '$BUILD_DIR/mongo/db/service_context_noop_init', + '$BUILD_DIR/mongo/transport/transport_layer_mock', + '$BUILD_DIR/mongo/db/auth/authcore', + '$BUILD_DIR/mongo/db/auth/authmocks', + '$BUILD_DIR/mongo/db/auth/authorization_session_for_test', 'logical_session_id', - 'logical_session_record', + 'logical_session_id_helpers', ], ) @@ -954,7 +922,6 @@ env.Library( 'keys_collection_manager', 'keys_collection_manager_zero', 'logical_clock', - 'signed_logical_session_id', ], ) @@ -1032,7 +999,6 @@ env.Library( ], LIBDEPS=[ 'logical_session_id', - 'logical_session_record', 'sessions_collection', 'server_parameters', 'service_liason', @@ -1050,10 +1016,10 @@ envWithAsio.CppUnitTest( 'keys_collection_manager', 'keys_collection_document', 'logical_clock', + 'logical_session_id_helpers', 'logical_session_cache', 'service_liason_mock', 'sessions_collection_mock', - 'signed_logical_session_id', ], ) diff --git a/src/mongo/db/auth/sasl_commands.cpp b/src/mongo/db/auth/sasl_commands.cpp index 57ad20647d8..f489e3792c6 100644 --- a/src/mongo/db/auth/sasl_commands.cpp +++ b/src/mongo/db/auth/sasl_commands.cpp @@ -85,7 +85,7 @@ public: virtual bool slaveOk() const { return true; } - virtual bool requiresAuth() { + bool requiresAuth() const override { return false; } }; @@ -111,7 +111,7 @@ public: virtual bool slaveOk() const { return true; } - virtual bool requiresAuth() { + bool requiresAuth() const override { return false; } }; diff --git a/src/mongo/db/auth/user.cpp b/src/mongo/db/auth/user.cpp index f98fa52550c..36039d2ef14 100644 --- a/src/mongo/db/auth/user.cpp +++ b/src/mongo/db/auth/user.cpp @@ -41,7 +41,22 @@ namespace mongo { -User::User(const UserName& name) : _name(name), _id(), _refCount(0), _isValid(1) {} +namespace { + +SHA256Block computeDigest(const UserName& name, const boost::optional<OID>& id) { + const auto& fn = name.getFullName(); + + if (id) { + return SHA256Block::computeHash({id->toCDR(), ConstDataRange(fn.c_str(), fn.size())}); + } else { + return SHA256Block::computeHash({ConstDataRange(fn.c_str(), fn.size())}); + } +}; + +} // namespace + +User::User(const UserName& name) + : _name(name), _id(), _digest(computeDigest(_name, _id)), _refCount(0), _isValid(1) {} User::~User() { dassert(_refCount == 0); @@ -55,6 +70,10 @@ const boost::optional<OID>& User::getID() const { return _id; } +const SHA256Block& User::getDigest() const { + return _digest; +} + RoleNameIterator User::getRoles() const { return makeRoleNameIteratorForContainer(_roles); } @@ -87,17 +106,9 @@ const ActionSet User::getActionsForResource(const ResourcePattern& resource) con return it->second.getActions(); } -User* User::clone() const { - std::unique_ptr<User> result(new User(_name)); - result->_id = _id; - result->_privileges = _privileges; - result->_roles = _roles; - result->_credentials = _credentials; - return result.release(); -} - void User::setID(boost::optional<OID> id) { _id = std::move(id); + _digest = computeDigest(_name, _id); } void User::setCredentials(const CredentialData& credentials) { diff --git a/src/mongo/db/auth/user.h b/src/mongo/db/auth/user.h index 4d179184323..7807043652d 100644 --- a/src/mongo/db/auth/user.h +++ b/src/mongo/db/auth/user.h @@ -32,6 +32,7 @@ #include "mongo/base/disallow_copying.h" #include "mongo/bson/oid.h" +#include "mongo/crypto/sha256_block.h" #include "mongo/db/auth/privilege.h" #include "mongo/db/auth/resource_pattern.h" #include "mongo/db/auth/restriction_set.h" @@ -93,6 +94,11 @@ public: const boost::optional<OID>& getID() const; /** + * Returns a digest of the user's identity + */ + const SHA256Block& getDigest() const; + + /** * Returns an iterator over the names of the user's direct roles */ RoleNameIterator getRoles() const; @@ -137,11 +143,6 @@ public: */ uint32_t getRefCount() const; - /** - * Clones this user into a new, valid User object with refcount of 0. - */ - User* clone() const; - // Mutators below. Mutation functions should *only* be called by the AuthorizationManager /** @@ -237,6 +238,9 @@ private: // an unset _id field to be a distinct value that will fail to compare to any other id value. boost::optional<OID> _id; + // Digest of the full username + SHA256Block _digest; + // Maps resource name to privilege on that resource ResourcePrivilegeMap _privileges; diff --git a/src/mongo/db/commands.h b/src/mongo/db/commands.h index 3f2346fe926..86fbec22161 100644 --- a/src/mongo/db/commands.h +++ b/src/mongo/db/commands.h @@ -148,6 +148,11 @@ public: virtual bool shouldAffectCommandCounter() const = 0; /** + * Return true if the command requires auth. + */ + virtual bool requiresAuth() const = 0; + + /** * Generates help text for this command. */ virtual void help(std::stringstream& help) const = 0; @@ -302,6 +307,10 @@ public: return true; } + bool requiresAuth() const override { + return true; + } + void help(std::stringstream& help) const override; Status explain(OperationContext* opCtx, diff --git a/src/mongo/db/commands/SConscript b/src/mongo/db/commands/SConscript index 57e5b5683fc..c6197fed134 100644 --- a/src/mongo/db/commands/SConscript +++ b/src/mongo/db/commands/SConscript @@ -85,7 +85,8 @@ env.Library( '$BUILD_DIR/mongo/db/lasterror', '$BUILD_DIR/mongo/db/log_process_details', '$BUILD_DIR/mongo/db/logical_session_cache', - '$BUILD_DIR/mongo/db/logical_session_record', + '$BUILD_DIR/mongo/db/logical_session_id', + '$BUILD_DIR/mongo/db/logical_session_id_helpers', '$BUILD_DIR/mongo/db/matcher/expressions', '$BUILD_DIR/mongo/db/repl/isself', '$BUILD_DIR/mongo/db/repl/repl_coordinator_global', diff --git a/src/mongo/db/commands/shutdown.h b/src/mongo/db/commands/shutdown.h index 90db47c3fb3..655d25b367f 100644 --- a/src/mongo/db/commands/shutdown.h +++ b/src/mongo/db/commands/shutdown.h @@ -39,7 +39,7 @@ class CmdShutdown : public BasicCommand { public: CmdShutdown() : BasicCommand("shutdown") {} - virtual bool requiresAuth() { + bool requiresAuth() const override { return true; } virtual bool adminOnly() const { diff --git a/src/mongo/db/commands/start_session_command.cpp b/src/mongo/db/commands/start_session_command.cpp index 529b93b2323..cd241c7a310 100644 --- a/src/mongo/db/commands/start_session_command.cpp +++ b/src/mongo/db/commands/start_session_command.cpp @@ -39,7 +39,7 @@ #include "mongo/db/jsobj.h" #include "mongo/db/logical_session_cache.h" #include "mongo/db/logical_session_id.h" -#include "mongo/db/logical_session_record.h" +#include "mongo/db/logical_session_id_helpers.h" #include "mongo/db/operation_context.h" #include "mongo/db/stats/top.h" @@ -79,57 +79,30 @@ public: const BSONObj& cmdObj, BSONObjBuilder& result) override { - boost::optional<OID> uid; auto client = opCtx->getClient(); - ServiceContext* serviceContext = client->getServiceContext(); - if (AuthorizationManager::get(serviceContext)->isAuthEnabled()) { - - UserName userName; - - auto authzSession = AuthorizationSession::get(client); - auto userNameItr = authzSession->getAuthenticatedUserNames(); - if (userNameItr.more()) { - userName = userNameItr.next(); - if (userNameItr.more()) { - return appendCommandStatus( - result, - Status(ErrorCodes::Unauthorized, - "must only be authenticated as exactly one user " - "to create a logical session")); - } - } else { - return appendCommandStatus(result, - Status(ErrorCodes::Unauthorized, - "must only be authenticated as exactly one user " - "to create a logical session")); - } - - User* user = authzSession->lookupUser(userName); - invariant(user); - uid = user->getID(); - } - auto lsCache = LogicalSessionCache::get(serviceContext); + boost::optional<LogicalSessionId> lsid; - auto statusWithSlsid = lsCache->signLsid(opCtx, LogicalSessionId::gen(), uid); - if (!statusWithSlsid.isOK()) { - return appendCommandStatus(result, statusWithSlsid.getStatus()); + try { + lsid = makeLogicalSessionId(opCtx); + } catch (...) { + auto status = exceptionToStatus(); + + return appendCommandStatus(result, status); } - auto slsid = statusWithSlsid.getValue(); - auto lsRecord = LogicalSessionRecord::makeAuthoritativeRecord( - slsid, serviceContext->getFastClockSource()->now()); - Status startSessionStatus = lsCache->startSession(std::move(slsid)); - appendCommandStatus(result, startSessionStatus); + auto lsCache = LogicalSessionCache::get(serviceContext); - if (startSessionStatus.isOK()) { - result.append("id", lsRecord.toBSON()); - result.append("timeoutMinutes", localLogicalSessionTimeoutMinutes); - return true; + Status startSessionStatus = lsCache->startSession(lsid.get()); + + if (!startSessionStatus.isOK()) { + return appendCommandStatus(result, startSessionStatus); } - return false; + makeLogicalSessionToClient(lsid.get()).serialize(&result); + + return true; } } startSessionCommand; diff --git a/src/mongo/db/logical_session_cache.cpp b/src/mongo/db/logical_session_cache.cpp index 0a93fd5addd..36c1625a69c 100644 --- a/src/mongo/db/logical_session_cache.cpp +++ b/src/mongo/db/logical_session_cache.cpp @@ -32,6 +32,7 @@ #include "mongo/db/logical_session_cache.h" +#include "mongo/db/logical_session_id.h" #include "mongo/db/operation_context.h" #include "mongo/db/server_parameters.h" #include "mongo/db/service_context.h" @@ -99,15 +100,15 @@ LogicalSessionCache::~LogicalSessionCache() { } } -Status LogicalSessionCache::fetchAndPromote(SignedLogicalSessionId slsid) { +Status LogicalSessionCache::fetchAndPromote(LogicalSessionId lsid) { // Search our local cache first - auto promoteRes = promote(slsid); + auto promoteRes = promote(lsid); if (promoteRes.isOK()) { return promoteRes; } // Cache miss, must fetch from the sessions collection. - auto res = _sessionsColl->fetchRecord(slsid); + auto res = _sessionsColl->fetchRecord(lsid); // If we got a valid record, add it to our cache. if (res.isOK()) { @@ -124,9 +125,9 @@ Status LogicalSessionCache::fetchAndPromote(SignedLogicalSessionId slsid) { return res.getStatus(); } -Status LogicalSessionCache::promote(SignedLogicalSessionId slsid) { +Status LogicalSessionCache::promote(LogicalSessionId lsid) { stdx::unique_lock<stdx::mutex> lk(_cacheMutex); - auto it = _cache.find(slsid.getLsid()); + auto it = _cache.find(lsid); if (it == _cache.end()) { return {ErrorCodes::NoSuchSession, "no matching session record found in the cache"}; } @@ -142,14 +143,14 @@ Status LogicalSessionCache::promote(SignedLogicalSessionId slsid) { return Status::OK(); } -Status LogicalSessionCache::startSession(SignedLogicalSessionId slsid) { - // Make sure the timestamp makes sense - auto authoritativeRecord = - LogicalSessionRecord::makeAuthoritativeRecord(slsid, _service->now()); +Status LogicalSessionCache::startSession(LogicalSessionId lsid) { + LogicalSessionRecord lsr; + lsr.setId(lsid); + lsr.setLastUse(_service->now()); // Attempt to insert into the sessions collection first. This collection enforces // unique session ids, so it will act as concurrency control for us. - auto res = _sessionsColl->insertRecord(authoritativeRecord); + auto res = _sessionsColl->insertRecord(lsr); if (!res.isOK()) { return res; } @@ -158,9 +159,9 @@ Status LogicalSessionCache::startSession(SignedLogicalSessionId slsid) { // conflicting record is not dead and is not equal to our record, an interloper // may have ended this session and then created a new one with the same id. // In this case, return a failure. - auto oldRecord = _addToCache(authoritativeRecord); + auto oldRecord = _addToCache(lsr); if (oldRecord) { - if (*oldRecord != authoritativeRecord) { + if (*oldRecord != lsr) { if (!_isDead(*oldRecord, _service->now())) { return {ErrorCodes::DuplicateSession, "session with this id already exists"}; } @@ -170,17 +171,6 @@ Status LogicalSessionCache::startSession(SignedLogicalSessionId slsid) { return Status::OK(); } -StatusWith<SignedLogicalSessionId> LogicalSessionCache::signLsid(OperationContext* opCtx, - const LogicalSessionId& id, - boost::optional<OID> userId) { - return _service->signLsid(opCtx, id, std::move(userId)); -} - -Status LogicalSessionCache::validateLsid(OperationContext* opCtx, - const SignedLogicalSessionId& slsid) { - return _service->validateLsid(opCtx, slsid); -} - void LogicalSessionCache::_refresh() { LogicalSessionIdSet activeSessions; LogicalSessionIdSet deadSessions; @@ -201,9 +191,9 @@ void LogicalSessionCache::_refresh() { for (auto& it : cacheCopy) { auto record = it.second; if (!_isDead(record, now)) { - activeSessions.insert(record.getSignedLsid().getLsid()); + activeSessions.insert(LogicalSessionId{record.getId()}); } else { - deadSessions.insert(record.getSignedLsid().getLsid()); + deadSessions.insert(LogicalSessionId{record.getId()}); } } @@ -260,7 +250,7 @@ bool LogicalSessionCache::_isDead(const LogicalSessionRecord& record, Date_t now boost::optional<LogicalSessionRecord> LogicalSessionCache::_addToCache( LogicalSessionRecord record) { stdx::unique_lock<stdx::mutex> lk(_cacheMutex); - return _cache.add(record.getSignedLsid().getLsid(), std::move(record)); + return _cache.add(record.getId(), std::move(record)); } } // namespace mongo diff --git a/src/mongo/db/logical_session_cache.h b/src/mongo/db/logical_session_cache.h index da3d521b02e..5d901c191ce 100644 --- a/src/mongo/db/logical_session_cache.h +++ b/src/mongo/db/logical_session_cache.h @@ -30,10 +30,8 @@ #include "mongo/base/status_with.h" #include "mongo/db/logical_session_id.h" -#include "mongo/db/logical_session_record.h" #include "mongo/db/service_liason.h" #include "mongo/db/sessions_collection.h" -#include "mongo/db/signed_logical_session_id.h" #include "mongo/db/time_proof_service.h" #include "mongo/platform/atomic_word.h" #include "mongo/stdx/thread.h" @@ -120,7 +118,7 @@ public: * * This method does not issue networking calls. */ - Status promote(SignedLogicalSessionId lsid); + Status promote(LogicalSessionId lsid); /** * If the cache contains a record for this LogicalSessionId, promotes it. @@ -129,7 +127,7 @@ public: * * This method may issue networking calls. */ - Status fetchAndPromote(SignedLogicalSessionId lsid); + Status fetchAndPromote(LogicalSessionId lsid); /** * Inserts a new authoritative session record into the cache. This method will @@ -137,22 +135,7 @@ public: * should only be used when starting new sessions and should not be used to * insert records for existing sessions. */ - Status startSession(SignedLogicalSessionId lsid); - - /** - * Generates and sets a signature for the fields in this LogicalSessionId. - * - * If this method is not able to acquire a key to perform the signature - * this call will return an error. - */ - StatusWith<SignedLogicalSessionId> signLsid(OperationContext* opCtx, - const LogicalSessionId& id, - boost::optional<OID> userId); - - /** - * Validates that this LogicalSessionId was signed with the correct key. - */ - Status validateLsid(OperationContext* opCtx, const SignedLogicalSessionId& lsid); + Status startSession(LogicalSessionId lsid); /** * Removes all local records in this cache. Does not remove the corresponding @@ -184,7 +167,7 @@ private: std::unique_ptr<SessionsCollection> _sessionsColl; stdx::mutex _cacheMutex; - LRUCache<LogicalSessionId, LogicalSessionRecord, LogicalSessionId::Hash> _cache; + LRUCache<LogicalSessionId, LogicalSessionRecord, LogicalSessionIdHash> _cache; }; } // namespace mongo diff --git a/src/mongo/db/logical_session_cache_test.cpp b/src/mongo/db/logical_session_cache_test.cpp index baba6814b34..381801cecab 100644 --- a/src/mongo/db/logical_session_cache_test.cpp +++ b/src/mongo/db/logical_session_cache_test.cpp @@ -32,7 +32,7 @@ #include "mongo/db/auth/user_name.h" #include "mongo/db/logical_session_cache.h" #include "mongo/db/logical_session_id.h" -#include "mongo/db/logical_session_record.h" +#include "mongo/db/logical_session_id_helpers.h" #include "mongo/db/service_liason_mock.h" #include "mongo/db/sessions_collection_mock.h" #include "mongo/stdx/future.h" @@ -97,128 +97,127 @@ private: // Test that session cache fetches new records from the sessions collection TEST_F(LogicalSessionCacheTest, CacheFetchesNewRecords) { - auto signedLsid = SignedLogicalSessionId::gen(); + auto lsid = makeLogicalSessionIdForTest(); // When the record is not present (and not in the sessions collection) returns an error - auto res = cache()->fetchAndPromote(signedLsid); + auto res = cache()->fetchAndPromote(lsid); ASSERT(!res.isOK()); // When the record is not present (but is in the sessions collection) returns it - sessions()->add(LogicalSessionRecord::makeAuthoritativeRecord(signedLsid, service()->now())); - res = cache()->fetchAndPromote(signedLsid); + sessions()->add(makeLogicalSessionRecord(lsid, service()->now())); + res = cache()->fetchAndPromote(lsid); ASSERT(res.isOK()); // When the record is present in the cache, returns it - sessions()->setFetchHook([](SignedLogicalSessionId id) -> StatusWith<LogicalSessionRecord> { + sessions()->setFetchHook([](LogicalSessionId id) -> StatusWith<LogicalSessionRecord> { // We should not be querying the sessions collection on the next call ASSERT(false); return {ErrorCodes::NoSuchSession, "no such session"}; }); - res = cache()->fetchAndPromote(signedLsid); + res = cache()->fetchAndPromote(lsid); ASSERT(res.isOK()); } // Test that the getFromCache method does not make calls to the sessions collection TEST_F(LogicalSessionCacheTest, TestCacheHitsOnly) { - auto signedLsid = SignedLogicalSessionId::gen(); + auto lsid = makeLogicalSessionIdForTest(); // When the record is not present (and not in the sessions collection), returns an error - auto res = cache()->promote(signedLsid); + auto res = cache()->promote(lsid); ASSERT(!res.isOK()); // When the record is not present (but is in the sessions collection), returns an error - sessions()->add(LogicalSessionRecord::makeAuthoritativeRecord(signedLsid, service()->now())); - res = cache()->promote(signedLsid); + sessions()->add(makeLogicalSessionRecord(lsid, service()->now())); + res = cache()->promote(lsid); ASSERT(!res.isOK()); // When the record is present, returns the owner - cache()->fetchAndPromote(signedLsid).transitional_ignore(); - res = cache()->promote(signedLsid); + cache()->fetchAndPromote(lsid).transitional_ignore(); + res = cache()->promote(lsid); ASSERT(res.isOK()); } // Test that fetching from the cache updates the lastUse date of records TEST_F(LogicalSessionCacheTest, FetchUpdatesLastUse) { - auto signedLsid = SignedLogicalSessionId::gen(); + auto lsid = makeLogicalSessionIdForTest(); auto start = service()->now(); // Insert the record into the sessions collection with 'start' - sessions()->add(LogicalSessionRecord::makeAuthoritativeRecord(signedLsid, start)); + sessions()->add(makeLogicalSessionRecord(lsid, start)); // Fast forward time and fetch service()->fastForward(Milliseconds(500)); ASSERT(start != service()->now()); - auto res = cache()->fetchAndPromote(signedLsid); + auto res = cache()->fetchAndPromote(lsid); ASSERT(res.isOK()); // Now that we fetched, lifetime of session should be extended service()->fastForward(kSessionTimeout - Milliseconds(500)); - res = cache()->fetchAndPromote(signedLsid); + res = cache()->fetchAndPromote(lsid); ASSERT(res.isOK()); // We fetched again, so lifetime extended again service()->fastForward(kSessionTimeout - Milliseconds(10)); - res = cache()->fetchAndPromote(signedLsid); + res = cache()->fetchAndPromote(lsid); ASSERT(res.isOK()); // Fast forward and hit-only fetch service()->fastForward(kSessionTimeout - Milliseconds(10)); - res = cache()->promote(signedLsid); + res = cache()->promote(lsid); ASSERT(res.isOK()); // Lifetime extended again service()->fastForward(Milliseconds(11)); - res = cache()->promote(signedLsid); + res = cache()->promote(lsid); ASSERT(res.isOK()); // Let record expire, we should not be able to get it from the cache service()->fastForward(kSessionTimeout + Milliseconds(1)); - res = cache()->promote(signedLsid); + res = cache()->promote(lsid); ASSERT(!res.isOK()); } // Test the startSession method TEST_F(LogicalSessionCacheTest, StartSession) { - auto signedLsid = SignedLogicalSessionId::gen(); + auto lsid = makeLogicalSessionIdForTest(); // Test starting a new session - auto res = cache()->startSession(signedLsid); + auto res = cache()->startSession(lsid); ASSERT(res.isOK()); - ASSERT(sessions()->has(signedLsid.getLsid())); + ASSERT(sessions()->has(lsid)); // Try to start a session that is already in the sessions collection and our // local cache, should fail - res = cache()->startSession(signedLsid); + res = cache()->startSession(lsid); ASSERT(!res.isOK()); // Try to start a session that is already in the sessions collection but // is not in our local cache, should fail - auto record2 = LogicalSessionRecord::makeAuthoritativeRecord(SignedLogicalSessionId::gen(), - service()->now()); - auto signedLsid2 = record2.getSignedLsid(); + auto record2 = makeLogicalSessionRecord(makeLogicalSessionIdForTest(), service()->now()); + auto lsid2 = record2.getId(); sessions()->add(std::move(record2)); - res = cache()->startSession(signedLsid2); + res = cache()->startSession(lsid2); ASSERT(!res.isOK()); // Try to start a session that has expired from our cache, and is no // longer in the sessions collection, should succeed service()->fastForward(Milliseconds(kSessionTimeout.count() + 5)); - sessions()->remove(signedLsid.getLsid()); - ASSERT(!sessions()->has(signedLsid.getLsid())); - res = cache()->startSession(signedLsid); + sessions()->remove(lsid); + ASSERT(!sessions()->has(lsid)); + res = cache()->startSession(lsid); ASSERT(res.isOK()); - ASSERT(sessions()->has(signedLsid.getLsid())); + ASSERT(sessions()->has(lsid)); } // Test that records in the cache are properly refreshed until they expire TEST_F(LogicalSessionCacheTest, CacheRefreshesOwnRecords) { // Insert two records into the cache - auto signedLsid1 = SignedLogicalSessionId::gen(); - auto signedLsid2 = SignedLogicalSessionId::gen(); - cache()->startSession(signedLsid1).transitional_ignore(); - cache()->startSession(signedLsid2).transitional_ignore(); + auto lsid1 = makeLogicalSessionIdForTest(); + auto lsid2 = makeLogicalSessionIdForTest(); + cache()->startSession(lsid1).transitional_ignore(); + cache()->startSession(lsid2).transitional_ignore(); stdx::promise<int> hitRefresh; auto refreshFuture = hitRefresh.get_future(); @@ -241,7 +240,7 @@ TEST_F(LogicalSessionCacheTest, CacheRefreshesOwnRecords) { auto refresh2Future = refresh2.get_future(); // Use one of the records - auto res = cache()->fetchAndPromote(signedLsid1); + auto res = cache()->fetchAndPromote(lsid1); ASSERT(res.isOK()); // Advance time so that one record expires @@ -258,25 +257,25 @@ TEST_F(LogicalSessionCacheTest, CacheRefreshesOwnRecords) { service()->fastForward(kSessionTimeout - kForceRefresh + Milliseconds(1)); refresh2Future.wait(); - ASSERT_EQ(refresh2Future.get(), signedLsid1.getLsid()); + ASSERT_EQ(refresh2Future.get(), lsid1); } // Test that cache deletes records that fail to refresh TEST_F(LogicalSessionCacheTest, CacheDeletesRecordsThatFailToRefresh) { // Put two sessions into the cache - auto signedLsid1 = SignedLogicalSessionId::gen(); - auto signedLsid2 = SignedLogicalSessionId::gen(); - cache()->startSession(signedLsid1).transitional_ignore(); - cache()->startSession(signedLsid2).transitional_ignore(); + auto lsid1 = makeLogicalSessionIdForTest(); + auto lsid2 = makeLogicalSessionIdForTest(); + cache()->startSession(lsid1).transitional_ignore(); + cache()->startSession(lsid2).transitional_ignore(); stdx::promise<void> hitRefresh; auto refreshFuture = hitRefresh.get_future(); // Record 1 fails to refresh - sessions()->setRefreshHook([&hitRefresh, &signedLsid1](LogicalSessionIdSet sessions) { + sessions()->setRefreshHook([&hitRefresh, &lsid1](LogicalSessionIdSet sessions) { ASSERT_EQ(sessions.size(), size_t(2)); hitRefresh.set_value(); - return LogicalSessionIdSet{signedLsid1.getLsid()}; + return LogicalSessionIdSet{lsid1}; }); // Force a refresh @@ -284,71 +283,71 @@ TEST_F(LogicalSessionCacheTest, CacheDeletesRecordsThatFailToRefresh) { refreshFuture.wait(); // Ensure that one record is still there and the other is gone - auto res = cache()->promote(signedLsid1); + auto res = cache()->promote(lsid1); ASSERT(!res.isOK()); - res = cache()->promote(signedLsid2); + res = cache()->promote(lsid2); ASSERT(res.isOK()); } // Test that we don't remove records that fail to refresh if they are active on the service TEST_F(LogicalSessionCacheTest, KeepActiveSessionAliveEvenIfRefreshFails) { // Put two sessions into the cache, one into the service - auto signedLsid1 = SignedLogicalSessionId::gen(); - auto signedLsid2 = SignedLogicalSessionId::gen(); - cache()->startSession(signedLsid1).transitional_ignore(); - service()->add(signedLsid1.getLsid()); - cache()->startSession(signedLsid2).transitional_ignore(); + auto lsid1 = makeLogicalSessionIdForTest(); + auto lsid2 = makeLogicalSessionIdForTest(); + cache()->startSession(lsid1).transitional_ignore(); + service()->add(lsid1); + cache()->startSession(lsid2).transitional_ignore(); stdx::promise<void> hitRefresh; auto refreshFuture = hitRefresh.get_future(); // SignedLsid 1 fails to refresh - sessions()->setRefreshHook([&hitRefresh, &signedLsid1](LogicalSessionIdSet sessions) { + sessions()->setRefreshHook([&hitRefresh, &lsid1](LogicalSessionIdSet sessions) { ASSERT_EQ(sessions.size(), size_t(2)); hitRefresh.set_value(); - return LogicalSessionIdSet{signedLsid1.getLsid()}; + return LogicalSessionIdSet{lsid1}; }); // Force a refresh service()->fastForward(kForceRefresh); refreshFuture.wait(); - // Ensure that both signedLsids are still there - auto res = cache()->promote(signedLsid1); + // Ensure that both lsids are still there + auto res = cache()->promote(lsid1); ASSERT(res.isOK()); - res = cache()->promote(signedLsid2); + res = cache()->promote(lsid2); ASSERT(res.isOK()); } -// Test that session cache properly expires signedLsids after 30 minutes of no use +// Test that session cache properly expires lsids after 30 minutes of no use TEST_F(LogicalSessionCacheTest, BasicSessionExpiration) { - // Insert a signedLsid - auto signedLsid = SignedLogicalSessionId::gen(); - cache()->startSession(signedLsid).transitional_ignore(); - auto res = cache()->promote(signedLsid); + // Insert a lsid + auto lsid = makeLogicalSessionIdForTest(); + cache()->startSession(lsid).transitional_ignore(); + auto res = cache()->promote(lsid); ASSERT(res.isOK()); // Force it to expire service()->fastForward(Milliseconds(kSessionTimeout.count() + 5)); // Check that it is no longer in the cache - res = cache()->promote(signedLsid); + res = cache()->promote(lsid); ASSERT(!res.isOK()); } // Test that we keep refreshing sessions that are active on the service TEST_F(LogicalSessionCacheTest, LongRunningQueriesAreRefreshed) { - auto signedLsid = SignedLogicalSessionId::gen(); + auto lsid = makeLogicalSessionIdForTest(); - // Insert one active signedLsid on the service, none in the cache - service()->add(signedLsid.getLsid()); + // Insert one active lsid on the service, none in the cache + service()->add(lsid); stdx::mutex mutex; stdx::condition_variable cv; int count = 0; - sessions()->setRefreshHook([&cv, &mutex, &count, &signedLsid](LogicalSessionIdSet sessions) { - ASSERT_EQ(*(sessions.begin()), signedLsid.getLsid()); + sessions()->setRefreshHook([&cv, &mutex, &count, &lsid](LogicalSessionIdSet sessions) { + ASSERT_EQ(*(sessions.begin()), lsid); { stdx::unique_lock<stdx::mutex> lk(mutex); count++; @@ -378,7 +377,7 @@ TEST_F(LogicalSessionCacheTest, LongRunningQueriesAreRefreshed) { // Wait until the next job has been scheduled waitUntilRefreshScheduled(); - // Force another refresh, check that it refreshes that active signedLsid again + // Force another refresh, check that it refreshes that active lsid again service()->fastForward(kForceRefresh); { stdx::unique_lock<stdx::mutex> lk(mutex); @@ -386,18 +385,18 @@ TEST_F(LogicalSessionCacheTest, LongRunningQueriesAreRefreshed) { } } -// Test that the set of signedLsids we refresh is a sum of cached + active signedLsids +// Test that the set of lsids we refresh is a sum of cached + active lsids TEST_F(LogicalSessionCacheTest, RefreshCachedAndServiceSignedLsidsTogether) { // Put one session into the cache, one into the service - auto signedLsid1 = SignedLogicalSessionId::gen(); - service()->add(signedLsid1.getLsid()); - auto signedLsid2 = SignedLogicalSessionId::gen(); - cache()->startSession(signedLsid2).transitional_ignore(); + auto lsid1 = makeLogicalSessionIdForTest(); + service()->add(lsid1); + auto lsid2 = makeLogicalSessionIdForTest(); + cache()->startSession(lsid2).transitional_ignore(); stdx::promise<void> hitRefresh; auto refreshFuture = hitRefresh.get_future(); - // Both signedLsids refresh + // Both lsids refresh sessions()->setRefreshHook([&hitRefresh](LogicalSessionIdSet sessions) { ASSERT_EQ(sessions.size(), size_t(2)); hitRefresh.set_value(); @@ -409,18 +408,18 @@ TEST_F(LogicalSessionCacheTest, RefreshCachedAndServiceSignedLsidsTogether) { refreshFuture.wait(); } -// Test large sets of cache-only session signedLsids +// Test large sets of cache-only session lsids TEST_F(LogicalSessionCacheTest, ManySignedLsidsInCacheRefresh) { int count = LogicalSessionCache::kLogicalSessionCacheDefaultCapacity; for (int i = 0; i < count; i++) { - auto signedLsid = SignedLogicalSessionId::gen(); - cache()->startSession(signedLsid).transitional_ignore(); + auto lsid = makeLogicalSessionIdForTest(); + cache()->startSession(lsid).transitional_ignore(); } stdx::promise<void> hitRefresh; auto refreshFuture = hitRefresh.get_future(); - // Check that all signedLsids refresh + // Check that all lsids refresh sessions()->setRefreshHook([&hitRefresh, &count](LogicalSessionIdSet sessions) { ASSERT_EQ(sessions.size(), size_t(count)); hitRefresh.set_value(); @@ -432,18 +431,18 @@ TEST_F(LogicalSessionCacheTest, ManySignedLsidsInCacheRefresh) { refreshFuture.wait(); } -// Test larger sets of service-only session signedLsids +// Test larger sets of service-only session lsids TEST_F(LogicalSessionCacheTest, ManyLongRunningSessionsRefresh) { int count = LogicalSessionCache::kLogicalSessionCacheDefaultCapacity; for (int i = 0; i < count; i++) { - auto lsid = LogicalSessionId::gen(); + auto lsid = makeLogicalSessionIdForTest(); service()->add(lsid); } stdx::promise<void> hitRefresh; auto refreshFuture = hitRefresh.get_future(); - // Check that all signedLsids refresh + // Check that all lsids refresh sessions()->setRefreshHook([&hitRefresh, &count](LogicalSessionIdSet sessions) { ASSERT_EQ(sessions.size(), size_t(count)); hitRefresh.set_value(); @@ -459,10 +458,10 @@ TEST_F(LogicalSessionCacheTest, ManyLongRunningSessionsRefresh) { TEST_F(LogicalSessionCacheTest, ManySessionsRefreshComboDeluxe) { int count = LogicalSessionCache::kLogicalSessionCacheDefaultCapacity; for (int i = 0; i < count; i++) { - auto lsid = LogicalSessionId::gen(); + auto lsid = makeLogicalSessionIdForTest(); service()->add(lsid); - auto lsid2 = SignedLogicalSessionId::gen(); + auto lsid2 = makeLogicalSessionIdForTest(); cache()->startSession(lsid2).transitional_ignore(); } @@ -471,7 +470,7 @@ TEST_F(LogicalSessionCacheTest, ManySessionsRefreshComboDeluxe) { int refreshes = 0; int nRefreshed = 0; - // Check that all signedLsids refresh successfully + // Check that all lsids refresh successfully sessions()->setRefreshHook( [&refreshes, &mutex, &cv, &nRefreshed](LogicalSessionIdSet sessions) { { @@ -531,7 +530,7 @@ TEST_F(LogicalSessionCacheTest, ManySessionsRefreshComboDeluxe) { cv.wait(lk, [&refreshes] { return refreshes == 3; }); } - // Since all but one signedLsid failed to refresh, third set should just have one signedLsid + // Since all but one lsid failed to refresh, third set should just have one lsid ASSERT_EQ(nRefreshed, 1); } diff --git a/src/mongo/db/logical_session_id.cpp b/src/mongo/db/logical_session_id.cpp index 36e4c6faf58..ad1dd8a496d 100644 --- a/src/mongo/db/logical_session_id.cpp +++ b/src/mongo/db/logical_session_id.cpp @@ -31,48 +31,18 @@ #include "mongo/db/logical_session_id.h" #include "mongo/bson/bsonobjbuilder.h" +#include "mongo/db/logical_session_cache.h" #include "mongo/util/assert_util.h" namespace mongo { -LogicalSessionId::LogicalSessionId() : LogicalSessionId(UUID::gen()) {} - -LogicalSessionId::LogicalSessionId(Logical_session_id&& lsid) { - static_cast<Logical_session_id&>(*this) = lsid; -} - -LogicalSessionId::LogicalSessionId(UUID id) { - setId(std::move(id)); -} - -LogicalSessionId LogicalSessionId::gen() { - return {UUID::gen()}; -} - -StatusWith<LogicalSessionId> LogicalSessionId::parse(const std::string& s) { - auto res = UUID::parse(s); - if (!res.isOK()) { - return res.getStatus(); - } - - return LogicalSessionId{std::move(res.getValue())}; -} - -LogicalSessionId LogicalSessionId::parse(const BSONObj& doc) { - IDLParserErrorContext ctx("logical session id"); +LogicalSessionId makeLogicalSessionIdForTest() { LogicalSessionId lsid; - lsid.parseProtected(ctx, doc); - return lsid; -} -BSONObj LogicalSessionId::toBSON() const { - BSONObjBuilder builder; - serialize(&builder); - return builder.obj(); -} + lsid.setId(UUID::gen()); + lsid.setUid(SHA256Block::computeHash({})); -std::string LogicalSessionId::toString() const { - return getId().toString(); + return lsid; } } // namespace mongo diff --git a/src/mongo/db/logical_session_id.h b/src/mongo/db/logical_session_id.h index 230b892d934..4984a57cc30 100644 --- a/src/mongo/db/logical_session_id.h +++ b/src/mongo/db/logical_session_id.h @@ -45,84 +45,66 @@ const TxnNumber kUninitializedTxnNumber = -1; class BSONObjBuilder; -/** - * A 128-bit unique identifier for a logical session. - */ -class LogicalSessionId : public Logical_session_id { -public: - LogicalSessionId(); - LogicalSessionId(Logical_session_id&& lsid); - - friend class Logical_session_id; - friend class Logical_session_record; - friend class SignedLogicalSessionId; - - /** - * Create and return a new LogicalSessionId with a random UUID. - */ - static LogicalSessionId gen(); - - /** - * If the given string represents a valid UUID, constructs and returns - * a new LogicalSessionId. Otherwise returns an error. - */ - static StatusWith<LogicalSessionId> parse(const std::string& s); - - /** - * Constructs a new LogicalSessionId out of a BSONObj. For IDL. - */ - static LogicalSessionId parse(const BSONObj& doc); - - /** - * Returns a string representation of this session id. - */ - std::string toString() const; - - /** - * Serialize this object to BSON. - */ - BSONObj toBSON() const; - - inline bool operator==(const LogicalSessionId& rhs) const { - return getId() == rhs.getId(); - } +class OperationContext; + +inline bool operator==(const LogicalSessionId& lhs, const LogicalSessionId& rhs) { + auto makeEqualityLens = [](const auto& lsid) { return std::tie(lsid.getId(), lsid.getUid()); }; + + return makeEqualityLens(lhs) == makeEqualityLens(rhs); +} - inline bool operator!=(const LogicalSessionId& rhs) const { - return !(*this == rhs); +inline bool operator!=(const LogicalSessionId& lhs, const LogicalSessionId& rhs) { + return !(lhs == rhs); +} + +inline bool operator==(const LogicalSessionRecord& lhs, const LogicalSessionRecord& rhs) { + return lhs.getId() == rhs.getId(); +} + +inline bool operator!=(const LogicalSessionRecord& lhs, const LogicalSessionRecord& rhs) { + return !(lhs == rhs); +} + +LogicalSessionId makeLogicalSessionIdForTest(); + +struct LogicalSessionIdHash { + std::size_t operator()(const LogicalSessionId& lsid) const { + return _hasher(lsid.getId()); } - /** - * Custom hasher so LogicalSessionIds can be used in unordered data structures. - * - * ex: std::unordered_set<LogicalSessionId, LogicalSessionId::Hash> lsidSet; - */ - struct Hash { - std::size_t operator()(const LogicalSessionId& lsid) const { - return _hasher(lsid.getId()); - } +private: + UUID::Hash _hasher; +}; - private: - UUID::Hash _hasher; - }; +struct LogicalSessionRecordHash { + std::size_t operator()(const LogicalSessionRecord& lsid) const { + return _hasher(lsid.getId().getId()); + } private: - /** - * Construct a LogicalSessionId from a UUID. - */ - LogicalSessionId(UUID id); + UUID::Hash _hasher; }; + inline std::ostream& operator<<(std::ostream& s, const LogicalSessionId& lsid) { - return (s << lsid.toString()); + return (s << lsid.getId() << " - " << lsid.getUid()); } inline StringBuilder& operator<<(StringBuilder& s, const LogicalSessionId& lsid) { - return (s << lsid.toString()); + return (s << lsid.getId().toString() << " - " << lsid.getUid().toString()); +} + +inline std::ostream& operator<<(std::ostream& s, const LogicalSessionFromClient& lsid) { + return (s << lsid.getId() << " - " << (lsid.getUid() ? lsid.getUid()->toString() : "")); +} + +inline StringBuilder& operator<<(StringBuilder& s, const LogicalSessionFromClient& lsid) { + return (s << lsid.getId() << " - " << (lsid.getUid() ? lsid.getUid()->toString() : "")); } /** * An alias for sets of session ids. */ -using LogicalSessionIdSet = stdx::unordered_set<LogicalSessionId, LogicalSessionId::Hash>; +using LogicalSessionIdSet = stdx::unordered_set<LogicalSessionId, LogicalSessionIdHash>; } // namespace mongo diff --git a/src/mongo/db/logical_session_id.idl b/src/mongo/db/logical_session_id.idl index b080f6d41e2..e60c57753c3 100644 --- a/src/mongo/db/logical_session_id.idl +++ b/src/mongo/db/logical_session_id.idl @@ -24,16 +24,10 @@ global: imports: - "mongo/idl/basic_types.idl" + - "mongo/crypto/sha256_block.idl" types: - UUIDIDL: - description: "IDL representation of the UUID type" - bson_serialization_type: object - cpp_type: "mongo::UUID" - deserializer: "mongo::UUID::parse" - serializer: "mongo::UUID::toBSON" - TxnNumber: description: "A strictly-increasing per-session counter, which indicates to which transaction of a given session does the specified command belong. The combination of @@ -50,18 +44,56 @@ types: structs: - logical_session_id: + LogicalSessionId: description: "A struct representing a LogicalSessionId" strict: true fields: - id: UUIDIDL + id: uuid + uid: sha256Block + + LogicalSessionToClient: + description: "A struct representing a LogicalSessionId to external clients" + strict: true + fields: + id: uuid + timeoutMinutes: int + + LogicalSessionRecord: + description: "A struct representing a LogicalSessionRecord" + strict: true + fields: + id: LogicalSessionId + lastUse: date + + LogicalSessionFromClient: + description: "A struct representing a LogicalSessionId from external clients" + strict: true + fields: + id: uuid + uid: + type: sha256Block + optional: true OperationSessionInfo: + description: "Parser for serializing sessionId/txnNumber combination" + strict: false + fields: + lsid: + type: LogicalSessionId + cpp_name: sessionId + optional: true + txnNumber: + description: "The transaction number relative to the session in which a particular write + operation executes." + type: TxnNumber + optional: true + + OperationSessionInfoFromClient: description: "Parser for pulling out the sessionId/txnNumber combination from commands" strict: false fields: lsid: - type: logical_session_id + type: LogicalSessionFromClient cpp_name: sessionId optional: true txnNumber: diff --git a/src/mongo/db/logical_session_id_helpers.cpp b/src/mongo/db/logical_session_id_helpers.cpp new file mode 100644 index 00000000000..e152e20bafb --- /dev/null +++ b/src/mongo/db/logical_session_id_helpers.cpp @@ -0,0 +1,155 @@ +/** + * Copyright (C) 2017 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. + */ + +#include "mongo/platform/basic.h" + +#include "mongo/db/logical_session_id.h" + +#include "mongo/db/auth/authorization_session.h" +#include "mongo/db/auth/user.h" +#include "mongo/db/logical_session_cache.h" +#include "mongo/db/operation_context.h" + +namespace mongo { + +namespace { + +/** + * This is a safe hash that will not collide with a username because all full usernames include an + * '@' character. + */ +const auto kNoAuthDigest = SHA256Block::computeHash(reinterpret_cast<const uint8_t*>(""), 0); + +SHA256Block lookupUserDigest(OperationContext* opCtx) { + auto client = opCtx->getClient(); + ServiceContext* serviceContext = client->getServiceContext(); + + if (AuthorizationManager::get(serviceContext)->isAuthEnabled()) { + UserName userName; + + auto authzSession = AuthorizationSession::get(client); + auto userNameItr = authzSession->getAuthenticatedUserNames(); + if (userNameItr.more()) { + userName = userNameItr.next(); + if (userNameItr.more()) { + uasserted(ErrorCodes::Unauthorized, + "must only be authenticated as exactly one user " + "to create a logical session"); + } + } else { + uasserted(ErrorCodes::Unauthorized, + "must only be authenticated as exactly one user " + "to create a logical session"); + } + + User* user = authzSession->lookupUser(userName); + invariant(user); + + return user->getDigest(); + } else { + return kNoAuthDigest; + } +} + +} // namespace + +LogicalSessionId makeLogicalSessionId(const LogicalSessionFromClient& fromClient, + OperationContext* opCtx) { + LogicalSessionId lsid; + + lsid.setId(fromClient.getId()); + + if (fromClient.getUid()) { + auto authSession = AuthorizationSession::get(opCtx->getClient()); + + uassert(ErrorCodes::Unauthorized, + "Unauthorized to set user digest in LogicalSessionId", + authSession->isAuthorizedForPrivilege( + Privilege(ResourcePattern::forClusterResource(), ActionType::impersonate))); + + lsid.setUid(*fromClient.getUid()); + } else { + lsid.setUid(lookupUserDigest(opCtx)); + } + + return lsid; +} + +LogicalSessionId makeLogicalSessionId(OperationContext* opCtx) { + LogicalSessionId id{}; + + id.setId(UUID::gen()); + id.setUid(lookupUserDigest(opCtx)); + + return id; +} + +LogicalSessionRecord makeLogicalSessionRecord(const LogicalSessionId& lsid, Date_t lastUse) { + LogicalSessionRecord lsr; + + lsr.setId(lsid); + lsr.setLastUse(lastUse); + + return lsr; +} + +LogicalSessionToClient makeLogicalSessionToClient(const LogicalSessionId& lsid) { + LogicalSessionToClient id; + id.setId(lsid.getId()); + id.setTimeoutMinutes(localLogicalSessionTimeoutMinutes); + + return id; +}; + +void initializeOperationSessionInfo(OperationContext* opCtx, + const BSONObj& requestBody, + bool requiresAuth) { + if (!requiresAuth) { + return; + } + + auto osi = OperationSessionInfoFromClient::parse(IDLParserErrorContext("OperationSessionInfo"), + requestBody); + + if (osi.getSessionId()) { + opCtx->setLogicalSessionId(makeLogicalSessionId(*(osi.getSessionId()), opCtx)); + } + + if (osi.getTxnNumber()) { + uassert(ErrorCodes::IllegalOperation, + "Transaction number requires a sessionId to be specified", + opCtx->getLogicalSessionId()); + uassert(ErrorCodes::BadValue, + "Transaction number cannot be negative", + *osi.getTxnNumber() >= 0); + + opCtx->setTxnNumber(*osi.getTxnNumber()); + } +} + +} // namespace mongo diff --git a/src/mongo/db/logical_session_id_helpers.h b/src/mongo/db/logical_session_id_helpers.h new file mode 100644 index 00000000000..5f1ff3c854e --- /dev/null +++ b/src/mongo/db/logical_session_id_helpers.h @@ -0,0 +1,55 @@ +/** + * Copyright (C) 2017 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 "mongo/db/logical_session_id.h" + +namespace mongo { + +LogicalSessionId makeLogicalSessionId(const LogicalSessionFromClient& lsid, + OperationContext* opCtx); +LogicalSessionId makeLogicalSessionId(OperationContext* opCtx); +LogicalSessionRecord makeLogicalSessionRecord(const LogicalSessionId& lsid, Date_t lastUse); +LogicalSessionToClient makeLogicalSessionToClient(const LogicalSessionId& lsid); + +/** + * Parses the session information from the body of a request and installs it on the current + * operation context. Must only be called once per operation and should be done right in the + * beginning. + * + * Throws if the sessionId/txnNumber combination is not properly formatted. + * + * requiresAuth specifies if the command we're initializing operationSessionInfo for requires + * authorization or not. This can be determined by invoking ->requiresAuth() on the parsed command. + */ +void initializeOperationSessionInfo(OperationContext* opCtx, + const BSONObj& requestBody, + bool requiresAuth); + +} // namespace mongo diff --git a/src/mongo/db/logical_session_id_test.cpp b/src/mongo/db/logical_session_id_test.cpp index c9e86639c4a..410292ef801 100644 --- a/src/mongo/db/logical_session_id_test.cpp +++ b/src/mongo/db/logical_session_id_test.cpp @@ -30,56 +30,225 @@ #include <string> -#include "mongo/bson/bsonmisc.h" -#include "mongo/bson/bsonobj.h" -#include "mongo/bson/bsonobjbuilder.h" -#include "mongo/bson/bsontypes.h" #include "mongo/db/logical_session_id.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/authorization_session_for_test.h" +#include "mongo/db/auth/authz_manager_external_state_mock.h" +#include "mongo/db/auth/authz_session_external_state_mock.h" +#include "mongo/db/auth/user.h" +#include "mongo/db/jsobj.h" +#include "mongo/db/logical_session_id_helpers.h" +#include "mongo/db/operation_context_noop.h" +#include "mongo/db/server_options.h" +#include "mongo/db/service_context_noop.h" +#include "mongo/transport/session.h" +#include "mongo/transport/transport_layer_mock.h" #include "mongo/unittest/unittest.h" -#include "mongo/util/uuid.h" namespace mongo { namespace { -TEST(LogicalSessionIdTest, ToAndFromStringTest) { - // Round-trip a UUID string - auto uuid = UUID::gen(); - auto uuidString = uuid.toString(); +class LogicalSessionIdTest : public ::mongo::unittest::Test { +public: + AuthzManagerExternalStateMock* managerState; + transport::TransportLayerMock transportLayer; + transport::SessionHandle session; + ServiceContextNoop serviceContext; + ServiceContext::UniqueClient client; + ServiceContext::UniqueOperationContext _opCtx; + AuthzSessionExternalStateMock* sessionState; + AuthorizationManager* authzManager; + AuthorizationSessionForTest* authzSession; + + void setUp() { + serverGlobalParams.featureCompatibility.version.store( + ServerGlobalParams::FeatureCompatibility::Version::k36); + session = transportLayer.createSession(); + client = serviceContext.makeClient("testClient", session); + RestrictionEnvironment::set( + session, stdx::make_unique<RestrictionEnvironment>(SockAddr(), SockAddr())); + _opCtx = client->makeOperationContext(); + auto localManagerState = stdx::make_unique<AuthzManagerExternalStateMock>(); + managerState = localManagerState.get(); + managerState->setAuthzVersion(AuthorizationManager::schemaVersion26Final); + auto uniqueAuthzManager = + stdx::make_unique<AuthorizationManager>(std::move(localManagerState)); + authzManager = uniqueAuthzManager.get(); + AuthorizationManager::set(&serviceContext, std::move(uniqueAuthzManager)); + auto localSessionState = stdx::make_unique<AuthzSessionExternalStateMock>(authzManager); + sessionState = localSessionState.get(); + + auto localauthzSession = + stdx::make_unique<AuthorizationSessionForTest>(std::move(localSessionState)); + authzSession = localauthzSession.get(); + + AuthorizationSession::set(client.get(), std::move(localauthzSession)); + authzManager->setAuthEnabled(true); + } + + User* addSimpleUser(UserName un) { + ASSERT_OK(managerState->insertPrivilegeDocument( + _opCtx.get(), + BSON("user" << un.getUser() << "db" << un.getDB() << "credentials" << BSON("MONGODB-CR" + << "a") + << "roles" + << BSON_ARRAY(BSON("role" + << "readWrite" + << "db" + << "test"))), + BSONObj())); + ASSERT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), un)); + return authzSession->lookupUser(un); + } + + User* addClusterUser(UserName un) { + ASSERT_OK(managerState->insertPrivilegeDocument( + _opCtx.get(), + BSON("user" << un.getUser() << "db" << un.getDB() << "credentials" << BSON("MONGODB-CR" + << "a") + << "roles" + << BSON_ARRAY(BSON("role" + << "__system" + << "db" + << "admin"))), + BSONObj())); + ASSERT_OK(authzSession->addAndAuthorizeUser(_opCtx.get(), un)); + return authzSession->lookupUser(un); + } +}; + +TEST_F(LogicalSessionIdTest, ConstructorFromClientWithoutPassedUid) { + auto id = UUID::gen(); + User* user = addSimpleUser(UserName("simple", "test")); + + LogicalSessionFromClient req; + req.setId(id); + + LogicalSessionId lsid = makeLogicalSessionId(req, _opCtx.get()); + ASSERT_EQ(lsid.getId(), id); + ASSERT_EQ(lsid.getUid(), user->getDigest()); +} + +TEST_F(LogicalSessionIdTest, ConstructorFromClientWithoutPassedUidAndWithoutAuthedUser) { + auto id = UUID::gen(); + + LogicalSessionFromClient req; + req.setId(id); + + ASSERT_THROWS(makeLogicalSessionId(req, _opCtx.get()), UserException); +} + +TEST_F(LogicalSessionIdTest, ConstructorFromClientWithPassedUidWithPermissions) { + auto id = UUID::gen(); + auto uid = SHA256Block{}; + addClusterUser(UserName("cluster", "test")); - auto res = LogicalSessionId::parse(uuidString); - ASSERT(res.isOK()); + LogicalSessionFromClient req; + req.setId(id); + req.setUid(uid); - auto lsidString = res.getValue().toString(); - ASSERT_EQUALS(uuidString, lsidString); + LogicalSessionId lsid = makeLogicalSessionId(req, _opCtx.get()); - // Test with a bad string - res = LogicalSessionId::parse("not a session id!"); - ASSERT(!res.isOK()); + ASSERT_EQ(lsid.getId(), id); + ASSERT_EQ(lsid.getUid(), uid); } -TEST(LogicalSessionIdTest, FromBSONTest) { - auto uuid = UUID::gen(); +TEST_F(LogicalSessionIdTest, ConstructorFromClientWithPassedUidWithoutAuthedUser) { + auto id = UUID::gen(); + auto uid = SHA256Block{}; + + LogicalSessionFromClient req; + req.setId(id); + req.setUid(uid); + + ASSERT_THROWS(makeLogicalSessionId(req, _opCtx.get()), UserException); +} + +TEST_F(LogicalSessionIdTest, ConstructorFromClientWithPassedUidWithoutPermissions) { + auto id = UUID::gen(); + auto uid = SHA256Block{}; + addSimpleUser(UserName("simple", "test")); + + LogicalSessionFromClient req; + req.setId(id); + req.setUid(uid); - BSONObjBuilder b; - b.append("id", uuid.toBSON()); - auto bson = b.done(); + ASSERT_THROWS(makeLogicalSessionId(req, _opCtx.get()), UserException); +} + +TEST_F(LogicalSessionIdTest, GenWithUser) { + User* user = addSimpleUser(UserName("simple", "test")); + auto lsid = makeLogicalSessionId(_opCtx.get()); + + ASSERT_EQ(lsid.getUid(), user->getDigest()); +} + +TEST_F(LogicalSessionIdTest, GenWithMultipleAuthedUsers) { + addSimpleUser(UserName("simple", "test")); + addSimpleUser(UserName("simple", "test2")); + + ASSERT_THROWS(makeLogicalSessionId(_opCtx.get()), UserException); +} + +TEST_F(LogicalSessionIdTest, GenWithoutAuthedUser) { + ASSERT_THROWS(makeLogicalSessionId(_opCtx.get()), UserException); +} + +TEST_F(LogicalSessionIdTest, InitializeOperationSessionInfo_NoSessionIdNoTransactionNumber) { + addSimpleUser(UserName("simple", "test")); + initializeOperationSessionInfo(_opCtx.get(), BSON("TestCmd" << 1), true); + + ASSERT(!_opCtx->getLogicalSessionId()); + ASSERT(!_opCtx->getTxnNumber()); +} + +TEST_F(LogicalSessionIdTest, InitializeOperationSessionInfo_SessionIdNoTransactionNumber) { + addSimpleUser(UserName("simple", "test")); + LogicalSessionFromClient lsid{}; + lsid.setId(UUID::gen()); + + initializeOperationSessionInfo(_opCtx.get(), + BSON("TestCmd" << 1 << "lsid" << lsid.toBSON() << "OtherField" + << "TestField"), + true); + + ASSERT(_opCtx->getLogicalSessionId()); + ASSERT_EQ(lsid.getId(), _opCtx->getLogicalSessionId()->getId()); + + ASSERT(!_opCtx->getTxnNumber()); +} + +TEST_F(LogicalSessionIdTest, InitializeOperationSessionInfo_MissingSessionIdWithTransactionNumber) { + addSimpleUser(UserName("simple", "test")); + ASSERT_THROWS_CODE( + initializeOperationSessionInfo(_opCtx.get(), + BSON("TestCmd" << 1 << "txnNumber" << 100LL << "OtherField" + << "TestField"), + true), + UserException, + ErrorCodes::IllegalOperation); +} - auto lsid = LogicalSessionId::parse(bson); - ASSERT_EQUALS(lsid.toString(), uuid.toString()); +TEST_F(LogicalSessionIdTest, InitializeOperationSessionInfo_SessionIdAndTransactionNumber) { + addSimpleUser(UserName("simple", "test")); + LogicalSessionFromClient lsid; + lsid.setId(UUID::gen()); - // Dump back to BSON, make sure we get the same thing - auto bsonDump = lsid.toBSON(); - ASSERT_EQ(bsonDump.woCompare(bson), 0); + initializeOperationSessionInfo( + _opCtx.get(), + BSON("TestCmd" << 1 << "lsid" << lsid.toBSON() << "txnNumber" << 100LL << "OtherField" + << "TestField"), + true); - // Try parsing mal-formatted bson objs - ASSERT_THROWS(LogicalSessionId::parse(BSON("hi" - << "there")), - UserException); + ASSERT(_opCtx->getLogicalSessionId()); + ASSERT_EQ(lsid.getId(), _opCtx->getLogicalSessionId()->getId()); - ASSERT_THROWS(LogicalSessionId::parse(BSON("id" - << "not a session id!")), - UserException); - ASSERT_THROWS(LogicalSessionId::parse(BSON("id" << 14)), UserException); + ASSERT(_opCtx->getTxnNumber()); + ASSERT_EQ(100, *_opCtx->getTxnNumber()); } } // namespace diff --git a/src/mongo/db/logical_session_record.cpp b/src/mongo/db/logical_session_record.cpp deleted file mode 100644 index 51ca629962b..00000000000 --- a/src/mongo/db/logical_session_record.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright (C) 2017 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. - */ - -#include "mongo/platform/basic.h" - -#include <utility> - -#include "mongo/db/logical_session_record.h" - -#include "mongo/bson/bsonobjbuilder.h" - -namespace mongo { - -StatusWith<LogicalSessionRecord> LogicalSessionRecord::parse(const BSONObj& bson) { - try { - IDLParserErrorContext ctxt("logical session record"); - LogicalSessionRecord record; - record.parseProtected(ctxt, bson); - return record; - } catch (std::exception e) { - return exceptionToStatus(); - } -} - -LogicalSessionRecord LogicalSessionRecord::makeAuthoritativeRecord(SignedLogicalSessionId id, - Date_t now) { - return LogicalSessionRecord(std::move(id), std::move(now)); -} - -BSONObj LogicalSessionRecord::toBSON() const { - BSONObjBuilder builder; - serialize(&builder); - return builder.obj(); -} - -std::string LogicalSessionRecord::toString() const { - return str::stream() << "LogicalSessionRecord" - << " Id: '" << getSignedLsid() << "'" - << " Last-use: " << getLastUse().toString(); -} - -LogicalSessionRecord::LogicalSessionRecord(SignedLogicalSessionId id, Date_t now) { - setSignedLsid(std::move(id)); - setLastUse(now); -} - -} // namespace mongo diff --git a/src/mongo/db/logical_session_record.h b/src/mongo/db/logical_session_record.h deleted file mode 100644 index 6d8662327ab..00000000000 --- a/src/mongo/db/logical_session_record.h +++ /dev/null @@ -1,99 +0,0 @@ -/** - * Copyright (C) 2017 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 <boost/optional.hpp> -#include <utility> - -#include "mongo/bson/bsonobj.h" -#include "mongo/db/logical_session_record_gen.h" -#include "mongo/db/signed_logical_session_id.h" -#include "mongo/util/time_support.h" - -namespace mongo { - -/** - * This class is the in-memory representation of a logical session record. - * - * The BSON representation of a session record follows this form: - * - * { - * lsid : SignedLogicalSessionId, - * lastUse : Date_t - * } - */ -class LogicalSessionRecord : public Logical_session_record { -public: - /** - * Constructs and returns a LogicalSessionRecord from a BSON representation, - * or throws an error. For IDL. - */ - static StatusWith<LogicalSessionRecord> parse(const BSONObj& bson); - - /** - * Construct a new record, for a new session that does not yet have an - * authoritative record in the sessions collection. This method should - * only be used when the caller is intending to make a new authoritative - * record and subsequently insert that record into the sessions collection. - */ - static LogicalSessionRecord makeAuthoritativeRecord(SignedLogicalSessionId id, Date_t now); - - /** - * Return a BSON representation of this session record. - */ - BSONObj toBSON() const; - - /** - * Return a string represenation of this session record. - */ - std::string toString() const; - - inline bool operator==(const LogicalSessionRecord& rhs) const { - return getSignedLsid() == rhs.getSignedLsid(); - } - - inline bool operator!=(const LogicalSessionRecord& rhs) const { - return !(*this == rhs); - } - -private: - LogicalSessionRecord() = default; - - LogicalSessionRecord(SignedLogicalSessionId id, Date_t now); -}; - -inline std::ostream& operator<<(std::ostream& s, const LogicalSessionRecord& record) { - return (s << record.toString()); -} - -inline StringBuilder& operator<<(StringBuilder& s, const LogicalSessionRecord& record) { - return (s << record.toString()); -} - -} // namespace mongo diff --git a/src/mongo/db/logical_session_record.idl b/src/mongo/db/logical_session_record.idl deleted file mode 100644 index 25e2ac65b76..00000000000 --- a/src/mongo/db/logical_session_record.idl +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (C) 2017 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/>. -# - -# This IDL file describes the BSON format for a LogicalSessionRecord, and -# handles the serialization to and deserialization from its BSON representation -# for that class. - -global: - cpp_namespace: "mongo" - cpp_includes: - - "mongo/db/logical_session_id.h" - - "mongo/db/signed_logical_session_id.h" - -imports: - - "mongo/idl/basic_types.idl" - - "mongo/db/signed_logical_session_id.idl" - -types: - SignedLogicalSessionIdIDL: - description: "IDL representation of the SignedLogicalSessionId cpp type" - bson_serialization_type: object - cpp_type: "mongo::SignedLogicalSessionId" - deserializer: "mongo::SignedLogicalSessionId::parse" - serializer: "mongo::SignedLogicalSessionId::toBSON" - -structs: - - logical_session_record: - description: "A struct representing a LogicalSessionRecord" - fields: - signedLsid: - description: "The id for this session record" - type: SignedLogicalSessionIdIDL - lastUse: - description: "The time at which this record was last used. Note: the date expressed in this in-memory record object may not match the date on the authoritative record for this session, which is stored in the sessions collection." - type: date diff --git a/src/mongo/db/logical_session_record_test.cpp b/src/mongo/db/logical_session_record_test.cpp deleted file mode 100644 index 659d23efa5b..00000000000 --- a/src/mongo/db/logical_session_record_test.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Copyright (C) 2017 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. - */ - -#include "mongo/platform/basic.h" - - -#include "mongo/bson/bsonmisc.h" -#include "mongo/bson/bsonobj.h" -#include "mongo/bson/bsonobjbuilder.h" -#include "mongo/bson/bsontypes.h" -#include "mongo/db/logical_session_id.h" -#include "mongo/db/logical_session_record.h" -#include "mongo/db/logical_session_record_gen.h" -#include "mongo/unittest/unittest.h" - -namespace mongo { -namespace { - -TEST(LogicalSessionRecordTest, ToAndFromBSONTest) { - // Round-trip a BSON obj - auto lsid = LogicalSessionId::gen(); - SignedLogicalSessionId slsid{lsid, boost::none, 1, SHA1Block{}}; - auto lastUse = Date_t::now(); - auto bson = BSON("signedLsid" << slsid.toBSON() << "lastUse" << lastUse); - - // Make a session record out of this - auto res = LogicalSessionRecord::parse(bson); - ASSERT(res.isOK()); - auto record = res.getValue(); - - ASSERT_EQ(record.getSignedLsid(), slsid); - ASSERT_EQ(record.getLastUse(), lastUse); - - // Dump back to bson, make sure we get the same thing - auto bsonDump = record.toBSON(); - ASSERT_EQ(bsonDump.woCompare(bson), 0); -} - -} // namespace -} // namespace mongo diff --git a/src/mongo/db/operation_context.cpp b/src/mongo/db/operation_context.cpp index d04760cd69d..848f1319ef0 100644 --- a/src/mongo/db/operation_context.cpp +++ b/src/mongo/db/operation_context.cpp @@ -393,25 +393,4 @@ Date_t OperationContext::getExpirationDateForWaitForValue(Milliseconds waitFor) return getServiceContext()->getPreciseClockSource()->now() + waitFor; } -void initializeOperationSessionInfo(OperationContext* opCtx, const BSONObj& requestBody) { - auto osi = - OperationSessionInfo::parse(IDLParserErrorContext("OperationSessionInfo"), requestBody); - - if (osi.getSessionId()) { - auto lsid = *osi.getSessionId(); - opCtx->setLogicalSessionId(LogicalSessionId(std::move(lsid))); - } - - if (osi.getTxnNumber()) { - uassert(ErrorCodes::IllegalOperation, - "Transaction number requires a sessionId to be specified", - opCtx->getLogicalSessionId()); - uassert(ErrorCodes::BadValue, - "Transaction number cannot be negative", - *osi.getTxnNumber() >= 0); - - opCtx->setTxnNumber(*osi.getTxnNumber()); - } -} - } // namespace mongo diff --git a/src/mongo/db/operation_context.h b/src/mongo/db/operation_context.h index 9bacdde641c..68641488d48 100644 --- a/src/mongo/db/operation_context.h +++ b/src/mongo/db/operation_context.h @@ -547,14 +547,4 @@ private: const bool _shouldReplicateWrites; }; } // namespace repl - -/** - * Parses the session information from the body of a request and installs it on the current - * operation context. Must only be called once per operation and should be done right in the - * beginning. - * - * Throws if the sessionId/txnNumber combination is not properly formatted. - */ -void initializeOperationSessionInfo(OperationContext* opCtx, const BSONObj& requestBody); - } // namespace mongo diff --git a/src/mongo/db/operation_context_test.cpp b/src/mongo/db/operation_context_test.cpp index 5174e599748..b5c8fe2c91d 100644 --- a/src/mongo/db/operation_context_test.cpp +++ b/src/mongo/db/operation_context_test.cpp @@ -89,7 +89,7 @@ TEST(OperationContextTest, SessionIdNoTransactionNumber) { auto client = serviceCtx->makeClient("OperationContextTest"); auto opCtx = client->makeOperationContext(); - const auto lsid = assertGet(LogicalSessionId::parse("00000000-abab-4000-8000-000000000000")); + const auto lsid = makeLogicalSessionIdForTest(); opCtx->setLogicalSessionId(lsid); ASSERT(opCtx->getLogicalSessionId()); @@ -103,7 +103,7 @@ TEST(OperationContextTest, SessionIdAndTransactionNumber) { auto client = serviceCtx->makeClient("OperationContextTest"); auto opCtx = client->makeOperationContext(); - const auto lsid = assertGet(LogicalSessionId::parse("00000000-abab-4000-8000-000000000000")); + const auto lsid = makeLogicalSessionIdForTest(); opCtx->setLogicalSessionId(lsid); opCtx->setTxnNumber(5); @@ -111,66 +111,6 @@ TEST(OperationContextTest, SessionIdAndTransactionNumber) { ASSERT_EQUALS(5, *opCtx->getTxnNumber()); } -TEST(OperationContextTest, InitializeOperationSessionInfo_NoSessionIdNoTransactionNumber) { - auto serviceCtx = stdx::make_unique<ServiceContextNoop>(); - auto client = serviceCtx->makeClient("OperationContextTest"); - auto opCtx = client->makeOperationContext(); - - initializeOperationSessionInfo(opCtx.get(), BSON("TestCmd" << 1)); - - ASSERT(!opCtx->getLogicalSessionId()); - ASSERT(!opCtx->getTxnNumber()); -} - -TEST(OperationContextTest, InitializeOperationSessionInfo_SessionIdNoTransactionNumber) { - auto serviceCtx = stdx::make_unique<ServiceContextNoop>(); - auto client = serviceCtx->makeClient("OperationContextTest"); - auto opCtx = client->makeOperationContext(); - - LogicalSessionId lsid; - - initializeOperationSessionInfo(opCtx.get(), - BSON("TestCmd" << 1 << "lsid" << lsid.toBSON() << "OtherField" - << "TestField")); - - ASSERT(opCtx->getLogicalSessionId()); - ASSERT_EQ(lsid, *opCtx->getLogicalSessionId()); - - ASSERT(!opCtx->getTxnNumber()); -} - -TEST(OperationContextTest, InitializeOperationSessionInfo_MissingSessionIdWithTransactionNumber) { - auto serviceCtx = stdx::make_unique<ServiceContextNoop>(); - auto client = serviceCtx->makeClient("OperationContextTest"); - auto opCtx = client->makeOperationContext(); - - ASSERT_THROWS_CODE( - initializeOperationSessionInfo(opCtx.get(), - BSON("TestCmd" << 1 << "txnNumber" << 100LL << "OtherField" - << "TestField")), - UserException, - ErrorCodes::IllegalOperation); -} - -TEST(OperationContextTest, InitializeOperationSessionInfo_SessionIdAndTransactionNumber) { - auto serviceCtx = stdx::make_unique<ServiceContextNoop>(); - auto client = serviceCtx->makeClient("OperationContextTest"); - auto opCtx = client->makeOperationContext(); - - LogicalSessionId lsid; - - initializeOperationSessionInfo( - opCtx.get(), - BSON("TestCmd" << 1 << "lsid" << lsid.toBSON() << "txnNumber" << 100LL << "OtherField" - << "TestField")); - - ASSERT(opCtx->getLogicalSessionId()); - ASSERT_EQ(lsid, *opCtx->getLogicalSessionId()); - - ASSERT(opCtx->getTxnNumber()); - ASSERT_EQ(100, *opCtx->getTxnNumber()); -} - TEST(OperationContextTest, OpCtxGroup) { OperationContextGroup group1; ASSERT_TRUE(group1.isEmpty()); diff --git a/src/mongo/db/repl/oplog.cpp b/src/mongo/db/repl/oplog.cpp index 951cf713c66..a3888344f9d 100644 --- a/src/mongo/db/repl/oplog.cpp +++ b/src/mongo/db/repl/oplog.cpp @@ -306,11 +306,8 @@ void appendSessionInfo(OperationContext* opCtx, BSONObjBuilder* builder, StmtId return; } - Logical_session_id lsid; - lsid.setId(logicalSessionId->getId()); - OperationSessionInfo sessionInfo; - sessionInfo.setSessionId(lsid); + sessionInfo.setSessionId(*logicalSessionId); sessionInfo.setTxnNumber(txnNum); sessionInfo.serialize(builder); diff --git a/src/mongo/db/repl/replication_info.cpp b/src/mongo/db/repl/replication_info.cpp index af7db98cf92..b8d5e9969e3 100644 --- a/src/mongo/db/repl/replication_info.cpp +++ b/src/mongo/db/repl/replication_info.cpp @@ -213,7 +213,7 @@ public: class CmdIsMaster : public BasicCommand { public: - virtual bool requiresAuth() { + bool requiresAuth() const override { return false; } virtual bool slaveOk() const { diff --git a/src/mongo/db/repl/rs_rollback_test.cpp b/src/mongo/db/repl/rs_rollback_test.cpp index 8d28647a343..0e750b4b958 100644 --- a/src/mongo/db/repl/rs_rollback_test.cpp +++ b/src/mongo/db/repl/rs_rollback_test.cpp @@ -1405,8 +1405,8 @@ DEATH_TEST_F(RSRollbackTest, LocalEntryWithTxnNumberWithoutStmtIdIsFatal, "invar const auto txnNumber = BSON("txnNumber" << 1LL); const auto noSessionIdOrStmtId = validOplogEntry.addField(txnNumber.firstElement()); - const auto uuid = UUID::gen(); - const auto sessionId = BSON("lsid" << BSON("id" << uuid.toBSON())); + const auto lsid = makeLogicalSessionIdForTest(); + const auto sessionId = BSON("lsid" << lsid.toBSON()); const auto noStmtId = noSessionIdOrStmtId.addField(sessionId.firstElement()); ASSERT_THROWS(updateFixUpInfoFromLocalOplogEntry(fui, noStmtId).transitional_ignore(), RSFatalException); @@ -1442,7 +1442,7 @@ TEST(RSRollbackTest, LocalEntryWithTxnNumberAddsTransactionTableDocToBeRefetched << "stmtId" << 1 << "lsid" - << BSON("id" << UUID::gen().toBSON())); + << makeLogicalSessionIdForTest().toBSON()); ASSERT_OK(updateFixUpInfoFromLocalOplogEntry(fui, entryWithTxnNumber)); // If txnNumber is present, the session transactions table document corresponding to the oplog diff --git a/src/mongo/db/service_entry_point_mongod.cpp b/src/mongo/db/service_entry_point_mongod.cpp index 89fd940be08..0802fcedc45 100644 --- a/src/mongo/db/service_entry_point_mongod.cpp +++ b/src/mongo/db/service_entry_point_mongod.cpp @@ -49,6 +49,7 @@ #include "mongo/db/lasterror.h" #include "mongo/db/logical_clock.h" #include "mongo/db/logical_session_id.h" +#include "mongo/db/logical_session_id_helpers.h" #include "mongo/db/logical_time_validator.h" #include "mongo/db/ops/write_ops.h" #include "mongo/db/ops/write_ops_exec.h" @@ -539,7 +540,7 @@ void execCommandDatabase(OperationContext* opCtx, rpc::readRequestMetadata(opCtx, request.body); rpc::TrackingMetadata::get(opCtx).initWithOperName(command->getName()); - initializeOperationSessionInfo(opCtx, request.body); + initializeOperationSessionInfo(opCtx, request.body, command->requiresAuth()); std::string dbname = request.getDatabase().toString(); uassert( diff --git a/src/mongo/db/service_liason.cpp b/src/mongo/db/service_liason.cpp index 96bb92d2b00..22edb98b5c2 100644 --- a/src/mongo/db/service_liason.cpp +++ b/src/mongo/db/service_liason.cpp @@ -36,73 +36,6 @@ namespace mongo { -namespace { - -const int kSignatureSize = sizeof(UUID) + sizeof(OID); - -SHA1Block computeSignature(const SignedLogicalSessionId* id, TimeProofService::Key key) { - // Write the uuid and user id to a block for signing. - char signatureBlock[kSignatureSize] = {0}; - DataRangeCursor cursor(signatureBlock, signatureBlock + kSignatureSize); - auto res = cursor.writeAndAdvance<ConstDataRange>(id->getLsid().getId().toCDR()); - invariant(res.isOK()); - if (auto userId = id->getUserId()) { - res = cursor.writeAndAdvance<ConstDataRange>(userId->toCDR()); - invariant(res.isOK()); - } - - // Compute the signature. - return SHA1Block::computeHmac( - key.data(), key.size(), reinterpret_cast<uint8_t*>(signatureBlock), kSignatureSize); -} - -KeysCollectionManagerZero kKeysCollectionManagerZero{"HMAC"}; - -} // namespace - ServiceLiason::~ServiceLiason() = default; -StatusWith<SignedLogicalSessionId> ServiceLiason::signLsid(OperationContext* opCtx, - const LogicalSessionId& lsid, - boost::optional<OID> userId) { - auto& keyManager = kKeysCollectionManagerZero; - - auto logicalTime = LogicalClock::get(_context())->getClusterTime(); - auto res = keyManager.getKeyForSigning(opCtx, logicalTime); - if (!res.isOK()) { - return res.getStatus(); - } - - SignedLogicalSessionId signedLsid; - signedLsid.setUserId(std::move(userId)); - signedLsid.setLsid(lsid); - - auto keyDoc = res.getValue(); - signedLsid.setKeyId(keyDoc.getKeyId()); - - auto signature = computeSignature(&signedLsid, keyDoc.getKey()); - signedLsid.setSignature(std::move(signature)); - - return signedLsid; -} - -Status ServiceLiason::validateLsid(OperationContext* opCtx, const SignedLogicalSessionId& id) { - auto& keyManager = kKeysCollectionManagerZero; - - // Attempt to get the correct key. - auto logicalTime = LogicalClock::get(_context())->getClusterTime(); - auto res = keyManager.getKeyForValidation(opCtx, id.getKeyId(), logicalTime); - if (!res.isOK()) { - return res.getStatus(); - } - - // Re-compute the signature, and see that it matches. - auto signature = computeSignature(&id, res.getValue().getKey()); - if (signature != id.getSignature()) { - return {ErrorCodes::NoSuchSession, "Signature validation failed."}; - } - - return Status::OK(); -} - } // namespace mongo diff --git a/src/mongo/db/service_liason.h b/src/mongo/db/service_liason.h index b74b9c0f8d4..ed7bcf0df86 100644 --- a/src/mongo/db/service_liason.h +++ b/src/mongo/db/service_liason.h @@ -29,7 +29,6 @@ #pragma once #include "mongo/db/logical_session_id.h" -#include "mongo/db/signed_logical_session_id.h" #include "mongo/stdx/functional.h" #include "mongo/util/periodic_runner.h" #include "mongo/util/time_support.h" @@ -77,21 +76,6 @@ public: */ virtual Date_t now() const = 0; - /** - * Generates and sets a signature for the fields in this LogicalSessionId. - * - * If this method is not able to acquire a key to perform the signature - * this call will return an error. - */ - StatusWith<SignedLogicalSessionId> signLsid(OperationContext* opCtx, - const LogicalSessionId& lsid, - boost::optional<OID> userId); - - /** - * Validates that this LogicalSessionId was signed with the correct key. - */ - Status validateLsid(OperationContext* opCtx, const SignedLogicalSessionId& id); - protected: /** * Returns the service context. diff --git a/src/mongo/db/session_catalog.h b/src/mongo/db/session_catalog.h index c6a67f6c44c..5d2ab0c17e1 100644 --- a/src/mongo/db/session_catalog.h +++ b/src/mongo/db/session_catalog.h @@ -129,7 +129,7 @@ private: using SessionRuntimeInfoMap = stdx::unordered_map<LogicalSessionId, std::shared_ptr<SessionRuntimeInfo>, - LogicalSessionId::Hash>; + LogicalSessionIdHash>; /** * Returns a session, previously clecked out through 'checkoutSession', available again. diff --git a/src/mongo/db/session_test.cpp b/src/mongo/db/session_test.cpp index b52068b7fb4..902a632a1ee 100644 --- a/src/mongo/db/session_test.cpp +++ b/src/mongo/db/session_test.cpp @@ -111,7 +111,7 @@ private: }; TEST_F(SessionTest, CanCreateNewSessionEntry) { - const auto sessionId = LogicalSessionId::gen(); + const auto sessionId = makeLogicalSessionIdForTest(); const TxnNumber txnNum = 20; Session txnState(sessionId); @@ -141,7 +141,7 @@ TEST_F(SessionTest, CanCreateNewSessionEntry) { } TEST_F(SessionTest, StartingOldTxnShouldAssert) { - const auto sessionId = LogicalSessionId::gen(); + const auto sessionId = makeLogicalSessionIdForTest(); const TxnNumber txnNum = 20; Session txnState(sessionId); @@ -154,7 +154,7 @@ TEST_F(SessionTest, StartingOldTxnShouldAssert) { } TEST_F(SessionTest, StartingNewSessionWithCompatibleEntryInStorage) { - const auto sessionId = LogicalSessionId::gen(); + const auto sessionId = makeLogicalSessionIdForTest(); const TxnNumber txnNum = 20; const Timestamp origTs(985, 15); @@ -193,7 +193,7 @@ TEST_F(SessionTest, StartingNewSessionWithCompatibleEntryInStorage) { } TEST_F(SessionTest, StartingNewSessionWithOlderEntryInStorageShouldUpdateEntry) { - const auto sessionId = LogicalSessionId::gen(); + const auto sessionId = makeLogicalSessionIdForTest(); TxnNumber txnNum = 20; const Timestamp origTs(985, 15); @@ -232,7 +232,7 @@ TEST_F(SessionTest, StartingNewSessionWithOlderEntryInStorageShouldUpdateEntry) } TEST_F(SessionTest, StartingNewSessionWithNewerEntryInStorageShouldAssert) { - const auto sessionId = LogicalSessionId::gen(); + const auto sessionId = makeLogicalSessionIdForTest(); TxnNumber txnNum = 20; const Timestamp origTs(985, 15); @@ -271,7 +271,7 @@ TEST_F(SessionTest, StartingNewSessionWithNewerEntryInStorageShouldAssert) { } TEST_F(SessionTest, StoreOpTime) { - const auto sessionId = LogicalSessionId::gen(); + const auto sessionId = makeLogicalSessionIdForTest(); const TxnNumber txnNum = 20; const Timestamp ts1(100, 42); @@ -337,7 +337,7 @@ TEST_F(SessionTest, StoreOpTime) { } TEST_F(SessionTest, CanBumpTransactionIdIfNewer) { - const auto sessionId = LogicalSessionId::gen(); + const auto sessionId = makeLogicalSessionIdForTest(); TxnNumber txnNum = 20; const Timestamp ts1(100, 42); @@ -397,7 +397,7 @@ TEST_F(SessionTest, CanBumpTransactionIdIfNewer) { } TEST_F(SessionTest, StartingNewSessionWithDroppedTableShouldAssert) { - const auto sessionId = LogicalSessionId::gen(); + const auto sessionId = makeLogicalSessionIdForTest(); const TxnNumber txnNum = 20; const auto& ns = NamespaceString::kSessionTransactionsTableNamespace; @@ -413,7 +413,7 @@ TEST_F(SessionTest, StartingNewSessionWithDroppedTableShouldAssert) { } TEST_F(SessionTest, SaveTxnProgressShouldAssertIfTableIsDropped) { - const auto sessionId = LogicalSessionId::gen(); + const auto sessionId = makeLogicalSessionIdForTest(); const TxnNumber txnNum = 20; const Timestamp ts1(100, 42); @@ -433,14 +433,14 @@ TEST_F(SessionTest, SaveTxnProgressShouldAssertIfTableIsDropped) { } TEST_F(SessionTest, TwoSessionsShouldBeIndependent) { - const auto sessionId1 = LogicalSessionId::gen(); + const auto sessionId1 = makeLogicalSessionIdForTest(); const TxnNumber txnNum1 = 20; const Timestamp ts1(1903, 42); Session txnState1(sessionId1); txnState1.begin(opCtx(), txnNum1); - const auto sessionId2 = LogicalSessionId::gen(); + const auto sessionId2 = makeLogicalSessionIdForTest(); const TxnNumber txnNum2 = 300; const Timestamp ts2(671, 5); @@ -498,7 +498,7 @@ TEST_F(SessionTest, TwoSessionsShouldBeIndependent) { } TEST_F(SessionTest, CheckStatementExecuted) { - const auto sessionId = LogicalSessionId::gen(); + const auto sessionId = makeLogicalSessionIdForTest(); const TxnNumber txnNum = 20; const StmtId stmtId = 5; diff --git a/src/mongo/db/session_txn_record.idl b/src/mongo/db/session_txn_record.idl index 42c6a107805..3a9dc02328d 100644 --- a/src/mongo/db/session_txn_record.idl +++ b/src/mongo/db/session_txn_record.idl @@ -35,14 +35,6 @@ imports: - "mongo/idl/basic_types.idl" - "mongo/db/logical_session_id.idl" -types: - LogicalSessionId: - description: "Logical_session_id" - bson_serialization_type: object - cpp_type: "mongo::LogicalSessionId" - deserializer: "mongo::LogicalSessionId::parse" - serializer: "mongo::LogicalSessionId::toBSON" - structs: sessionTxnRecord: description: "A document used for storing session transaction states." diff --git a/src/mongo/db/sessions_collection.h b/src/mongo/db/sessions_collection.h index 7692e634275..4a7503a293e 100644 --- a/src/mongo/db/sessions_collection.h +++ b/src/mongo/db/sessions_collection.h @@ -29,8 +29,6 @@ #pragma once #include "mongo/db/logical_session_id.h" -#include "mongo/db/logical_session_record.h" -#include "mongo/db/signed_logical_session_id.h" namespace mongo { @@ -48,7 +46,7 @@ public: * Returns a LogicalSessionRecord for the given session id. This method * may run networking operations on the calling thread. */ - virtual StatusWith<LogicalSessionRecord> fetchRecord(SignedLogicalSessionId id) = 0; + virtual StatusWith<LogicalSessionRecord> fetchRecord(LogicalSessionId id) = 0; /** * Inserts the given record into the sessions collection. This method may run diff --git a/src/mongo/db/sessions_collection_mock.cpp b/src/mongo/db/sessions_collection_mock.cpp index 1079605badd..832213e1223 100644 --- a/src/mongo/db/sessions_collection_mock.cpp +++ b/src/mongo/db/sessions_collection_mock.cpp @@ -65,8 +65,7 @@ void MockSessionsCollectionImpl::clearHooks() { _remove = stdx::bind(&MockSessionsCollectionImpl::_removeRecords, this, stdx::placeholders::_1); } -StatusWith<LogicalSessionRecord> MockSessionsCollectionImpl::fetchRecord( - SignedLogicalSessionId id) { +StatusWith<LogicalSessionRecord> MockSessionsCollectionImpl::fetchRecord(LogicalSessionId id) { return _fetch(std::move(id)); } @@ -84,7 +83,7 @@ void MockSessionsCollectionImpl::removeRecords(LogicalSessionIdSet sessions) { void MockSessionsCollectionImpl::add(LogicalSessionRecord record) { stdx::unique_lock<stdx::mutex> lk(_mutex); - _sessions.insert({record.getSignedLsid().getLsid(), std::move(record)}); + _sessions.insert({record.getId(), std::move(record)}); } void MockSessionsCollectionImpl::remove(LogicalSessionId lsid) { @@ -106,12 +105,11 @@ const MockSessionsCollectionImpl::SessionMap& MockSessionsCollectionImpl::sessio return _sessions; } -StatusWith<LogicalSessionRecord> MockSessionsCollectionImpl::_fetchRecord( - SignedLogicalSessionId id) { +StatusWith<LogicalSessionRecord> MockSessionsCollectionImpl::_fetchRecord(LogicalSessionId id) { stdx::unique_lock<stdx::mutex> lk(_mutex); // If we do not have this record, return an error - auto it = _sessions.find(id.getLsid()); + auto it = _sessions.find(id); if (it == _sessions.end()) { return {ErrorCodes::NoSuchSession, "No matching record in the sessions collection"}; } @@ -121,7 +119,7 @@ StatusWith<LogicalSessionRecord> MockSessionsCollectionImpl::_fetchRecord( Status MockSessionsCollectionImpl::_insertRecord(LogicalSessionRecord record) { stdx::unique_lock<stdx::mutex> lk(_mutex); - auto res = _sessions.insert({record.getSignedLsid().getLsid(), std::move(record)}); + auto res = _sessions.insert({record.getId(), std::move(record)}); // We should never try to insert the same record twice. In theory this could // happen because of a UUID conflict. diff --git a/src/mongo/db/sessions_collection_mock.h b/src/mongo/db/sessions_collection_mock.h index afa04bfd9db..870d0434991 100644 --- a/src/mongo/db/sessions_collection_mock.h +++ b/src/mongo/db/sessions_collection_mock.h @@ -29,7 +29,6 @@ #pragma once #include "mongo/db/logical_session_id.h" -#include "mongo/db/logical_session_record.h" #include "mongo/db/sessions_collection.h" #include "mongo/stdx/functional.h" #include "mongo/stdx/mutex.h" @@ -55,11 +54,11 @@ namespace mongo { class MockSessionsCollectionImpl { public: using SessionMap = - stdx::unordered_map<LogicalSessionId, LogicalSessionRecord, LogicalSessionId::Hash>; + stdx::unordered_map<LogicalSessionId, LogicalSessionRecord, LogicalSessionIdHash>; MockSessionsCollectionImpl(); - using FetchHook = stdx::function<StatusWith<LogicalSessionRecord>(SignedLogicalSessionId)>; + using FetchHook = stdx::function<StatusWith<LogicalSessionRecord>(LogicalSessionId)>; using InsertHook = stdx::function<Status(LogicalSessionRecord)>; using RefreshHook = stdx::function<LogicalSessionIdSet(LogicalSessionIdSet)>; using RemoveHook = stdx::function<void(LogicalSessionIdSet)>; @@ -74,7 +73,7 @@ public: void clearHooks(); // Forwarding methods from the MockSessionsCollection - StatusWith<LogicalSessionRecord> fetchRecord(SignedLogicalSessionId id); + StatusWith<LogicalSessionRecord> fetchRecord(LogicalSessionId id); Status insertRecord(LogicalSessionRecord record); LogicalSessionIdSet refreshSessions(LogicalSessionIdSet sessions); void removeRecords(LogicalSessionIdSet sessions); @@ -88,7 +87,7 @@ public: private: // Default implementations, may be overridden with custom hooks. - StatusWith<LogicalSessionRecord> _fetchRecord(SignedLogicalSessionId id); + StatusWith<LogicalSessionRecord> _fetchRecord(LogicalSessionId id); Status _insertRecord(LogicalSessionRecord record); LogicalSessionIdSet _refreshSessions(LogicalSessionIdSet sessions); void _removeRecords(LogicalSessionIdSet sessions); @@ -112,7 +111,7 @@ public: explicit MockSessionsCollection(std::shared_ptr<MockSessionsCollectionImpl> impl) : _impl(std::move(impl)) {} - StatusWith<LogicalSessionRecord> fetchRecord(SignedLogicalSessionId id) override { + StatusWith<LogicalSessionRecord> fetchRecord(LogicalSessionId id) override { return _impl->fetchRecord(std::move(id)); } diff --git a/src/mongo/db/signed_logical_session_id.cpp b/src/mongo/db/signed_logical_session_id.cpp deleted file mode 100644 index a284fb6344b..00000000000 --- a/src/mongo/db/signed_logical_session_id.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Copyright (C) 2017 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. - */ - -#include "mongo/platform/basic.h" - -#include "mongo/db/signed_logical_session_id.h" - -#include "mongo/bson/bsonobjbuilder.h" -#include "mongo/util/assert_util.h" - -namespace mongo { - -SignedLogicalSessionId SignedLogicalSessionId::gen() { - return SignedLogicalSessionId(); -} - -SignedLogicalSessionId::SignedLogicalSessionId() { - setLsid(LogicalSessionId::gen()); -} - -SignedLogicalSessionId::SignedLogicalSessionId(LogicalSessionId lsid, - boost::optional<OID> userId, - long long keyId, - SHA1Block signature) { - setLsid(std::move(lsid)); - setUserId(std::move(userId)); - setKeyId(std::move(keyId)); - setSignature(std::move(signature)); -} - -SignedLogicalSessionId SignedLogicalSessionId::parse(const BSONObj& doc) { - IDLParserErrorContext ctx("signed logical session id"); - SignedLogicalSessionId lsid; - lsid.parseProtected(ctx, doc); - return lsid; -} - -BSONObj SignedLogicalSessionId::toBSON() const { - BSONObjBuilder builder; - serialize(&builder); - return builder.obj(); -} - -std::string SignedLogicalSessionId::toString() const { - return getLsid().toString(); -} - -} // namespace mongo diff --git a/src/mongo/db/signed_logical_session_id.h b/src/mongo/db/signed_logical_session_id.h deleted file mode 100644 index 632f645c623..00000000000 --- a/src/mongo/db/signed_logical_session_id.h +++ /dev/null @@ -1,115 +0,0 @@ -/** - * Copyright (C) 2017 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 "mongo/base/status_with.h" -#include "mongo/bson/oid.h" -#include "mongo/db/logical_session_id.h" -#include "mongo/db/signed_logical_session_id_gen.h" -#include "mongo/stdx/unordered_set.h" -#include "mongo/util/uuid.h" - -namespace mongo { - -class BSONObjBuilder; -class OperationContext; - -/** - * An identifier for a logical session. A LogicalSessionId has the following components: - * - * - A 128-bit unique identifier (UUID) - * - An optional user id (ObjectId) - * - A key id (long long) - * - An HMAC signature (SHA1Block) - */ -class SignedLogicalSessionId : public Signed_logical_session_id { -public: - using Owner = boost::optional<OID>; - - friend class Logical_session_id; - friend class Logical_session_record; - - using keyIdType = long long; - - /** - * Create and return a new LogicalSessionId with a random UUID. This method - * should be used for testing only. The generated SignedLogicalSessionId will - * not be signed, and will have no owner. - */ - static SignedLogicalSessionId gen(); - - /** - * Creates a new SignedLogicalSessionId. - */ - SignedLogicalSessionId(LogicalSessionId lsid, - boost::optional<OID> userId, - long long keyId, - SHA1Block signature); - - /** - * Constructs a new LogicalSessionId out of a BSONObj. For IDL. - */ - static SignedLogicalSessionId parse(const BSONObj& doc); - - /** - * Returns a string representation of this session id. - */ - std::string toString() const; - - /** - * Serialize this object to BSON. - */ - BSONObj toBSON() const; - - inline bool operator==(const SignedLogicalSessionId& rhs) const { - return getLsid() == rhs.getLsid() && getUserId() == rhs.getUserId() && - getKeyId() == rhs.getKeyId() && getSignature() == rhs.getSignature(); - } - - inline bool operator!=(const SignedLogicalSessionId& rhs) const { - return !(*this == rhs); - } - - /** - * This constructor exists for IDL only. - */ - SignedLogicalSessionId(); -}; - -inline std::ostream& operator<<(std::ostream& s, const SignedLogicalSessionId& lsid) { - return (s << lsid.toString()); -} - -inline StringBuilder& operator<<(StringBuilder& s, const SignedLogicalSessionId& lsid) { - return (s << lsid.toString()); -} - -} // namespace mongo diff --git a/src/mongo/db/signed_logical_session_id.idl b/src/mongo/db/signed_logical_session_id.idl deleted file mode 100644 index 634d4731a52..00000000000 --- a/src/mongo/db/signed_logical_session_id.idl +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright (C) 2017 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/>. -# - -# This IDL file describes the BSON format for a LogicalSessionId, and -# handles the serialization to and deserialization from its BSON representation -# for that class. - -global: - cpp_namespace: "mongo" - cpp_includes: - - "mongo/util/uuid.h" - - "mongo/db/logical_session_id.h" - -imports: - - "mongo/crypto/sha1_block.idl" - - "mongo/db/logical_session_id.idl" - - "mongo/idl/basic_types.idl" - -types: - LogicalSessionIdIdl: - description: "IDL representation of the LogicalSessionId cpp type" - bson_serialization_type: object - cpp_type: "mongo::LogicalSessionId" - deserializer: "mongo::LogicalSessionId::parse" - serializer: "mongo::LogicalSessionId::toBSON" - -structs: - - signed_logical_session_id: - description: "A struct representing a SignedLogicalSessionId" - strict: true - fields: - lsid: LogicalSessionIdIdl - userId: - optional: true - type: objectid - keyId: long - signature: sha1Block diff --git a/src/mongo/db/signed_logical_session_id_test.cpp b/src/mongo/db/signed_logical_session_id_test.cpp deleted file mode 100644 index 2622d65f2f8..00000000000 --- a/src/mongo/db/signed_logical_session_id_test.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright (C) 2017 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. - */ - -#include "mongo/platform/basic.h" - -#include <string> - -#include "mongo/bson/bsonmisc.h" -#include "mongo/bson/bsonobj.h" -#include "mongo/bson/bsonobjbuilder.h" -#include "mongo/bson/bsontypes.h" -#include "mongo/crypto/sha1_block.h" -#include "mongo/db/logical_session_id.h" -#include "mongo/db/signed_logical_session_id.h" -#include "mongo/unittest/unittest.h" -#include "mongo/util/uuid.h" - -namespace mongo { -namespace { - -TEST(SignedLogicalSessionIdTest, ConstructWithLsid) { - auto lsid = LogicalSessionId::gen(); - SignedLogicalSessionId slsid(lsid, boost::none, 1, SHA1Block{}); - ASSERT_EQ(slsid.getLsid(), lsid); -} - -TEST(SignedLogicalSessionIdTest, FromBSONTest) { - auto lsid = LogicalSessionId::gen(); - - BSONObjBuilder b; - b.append("lsid", lsid.toBSON()); - b.append("keyId", 4ll); - char buffer[SHA1Block::kHashLength] = {0}; - b.appendBinData("signature", SHA1Block::kHashLength, BinDataGeneral, buffer); - auto bson = b.done(); - - auto slsid = SignedLogicalSessionId::parse(bson); - ASSERT_EQ(slsid.getLsid(), lsid); - - // Dump back to BSON, make sure we get the same thing - auto bsonDump = slsid.toBSON(); - ASSERT_EQ(bsonDump.woCompare(bson), 0); - - // Try parsing mal-formatted bson objs - ASSERT_THROWS(SignedLogicalSessionId::parse(BSON("hi" - << "there")), - UserException); - - ASSERT_THROWS(SignedLogicalSessionId::parse(BSON("lsid" - << "not a session id!")), - UserException); - ASSERT_THROWS(SignedLogicalSessionId::parse(BSON("lsid" << 14)), UserException); -} - -} // namespace -} // namespace mongo diff --git a/src/mongo/dbtests/cursor_manager_test.cpp b/src/mongo/dbtests/cursor_manager_test.cpp index 6abd9844de7..6505a64bea0 100644 --- a/src/mongo/dbtests/cursor_manager_test.cpp +++ b/src/mongo/dbtests/cursor_manager_test.cpp @@ -441,7 +441,7 @@ TEST_F(CursorManagerTestCustomOpCtx, LogicalSessionIdOnOperationCtxTest) { // Cursors created on an op ctx with a session id have a session id. { - auto lsid = LogicalSessionId::gen(); + auto lsid = makeLogicalSessionIdForTest(); auto opCtx2 = _queryServiceContext->makeOperationContext(lsid); auto pinned2 = makeCursor(opCtx2.get()); @@ -469,7 +469,7 @@ TEST_F(CursorManagerTestCustomOpCtx, CursorsWithoutSessions) { */ TEST_F(CursorManagerTestCustomOpCtx, OneCursorWithASession) { // Add a cursor with a session to the cursor manager. - auto lsid = LogicalSessionId::gen(); + auto lsid = makeLogicalSessionIdForTest(); auto opCtx = _queryServiceContext->makeOperationContext(lsid); auto pinned = makeCursor(opCtx.get()); @@ -501,7 +501,7 @@ TEST_F(CursorManagerTestCustomOpCtx, OneCursorWithASession) { */ TEST_F(CursorManagerTestCustomOpCtx, MultipleCursorsWithSameSession) { // Add two cursors on the same session to the cursor manager. - auto lsid = LogicalSessionId::gen(); + auto lsid = makeLogicalSessionIdForTest(); auto opCtx = _queryServiceContext->makeOperationContext(lsid); auto pinned = makeCursor(opCtx.get()); auto pinned2 = makeCursor(opCtx.get()); @@ -510,7 +510,7 @@ TEST_F(CursorManagerTestCustomOpCtx, MultipleCursorsWithSameSession) { auto cursorId2 = pinned2.getCursor()->cursorid(); // Retrieve all sessions - set should contain just lsid. - stdx::unordered_set<LogicalSessionId, LogicalSessionId::Hash> lsids; + stdx::unordered_set<LogicalSessionId, LogicalSessionIdHash> lsids; useCursorManager()->appendActiveSessions(&lsids); ASSERT_EQ(lsids.size(), size_t(1)); ASSERT(lsids.find(lsid) != lsids.end()); @@ -541,8 +541,8 @@ TEST_F(CursorManagerTestCustomOpCtx, MultipleCursorsWithSameSession) { * Test a manager with multiple cursors running inside of different sessions. */ TEST_F(CursorManagerTestCustomOpCtx, MultipleCursorsMultipleSessions) { - auto lsid1 = LogicalSessionId::gen(); - auto lsid2 = LogicalSessionId::gen(); + auto lsid1 = makeLogicalSessionIdForTest(); + auto lsid2 = makeLogicalSessionIdForTest(); CursorId cursor1; CursorId cursor2; diff --git a/src/mongo/s/commands/strategy.cpp b/src/mongo/s/commands/strategy.cpp index 90b9748732b..b6037760773 100644 --- a/src/mongo/s/commands/strategy.cpp +++ b/src/mongo/s/commands/strategy.cpp @@ -42,6 +42,7 @@ #include "mongo/db/auth/authorization_session.h" #include "mongo/db/commands.h" #include "mongo/db/logical_clock.h" +#include "mongo/db/logical_session_id_helpers.h" #include "mongo/db/logical_time_validator.h" #include "mongo/db/matcher/extensions_callback_noop.h" #include "mongo/db/namespace_string.h" @@ -256,12 +257,12 @@ void runAgainstRegistered(OperationContext* opCtx, return; } + initializeOperationSessionInfo(opCtx, request.body, c->requiresAuth()); + execCommandClient(opCtx, c, request, anObjBuilder); } void runCommand(OperationContext* opCtx, const OpMsgRequest& request, BSONObjBuilder&& builder) { - initializeOperationSessionInfo(opCtx, request.body); - // Handle command option maxTimeMS. uassert(ErrorCodes::InvalidOptions, "no such command option $maxTimeMs; use maxTimeMS instead", diff --git a/src/mongo/s/query/SConscript b/src/mongo/s/query/SConscript index 39bc9dcf017..f2a7b25cb28 100644 --- a/src/mongo/s/query/SConscript +++ b/src/mongo/s/query/SConscript @@ -131,6 +131,7 @@ env.Library( ], LIBDEPS=[ '$BUILD_DIR/mongo/base', + '$BUILD_DIR/mongo/db/logical_session_id', ], ) diff --git a/src/mongo/s/query/cluster_client_cursor_impl_test.cpp b/src/mongo/s/query/cluster_client_cursor_impl_test.cpp index 6c10cf18530..549a692d720 100644 --- a/src/mongo/s/query/cluster_client_cursor_impl_test.cpp +++ b/src/mongo/s/query/cluster_client_cursor_impl_test.cpp @@ -164,7 +164,7 @@ TEST(ClusterClientCursorImpl, LogicalSessionIdsOnCursors) { // Make a cursor with an lsid auto mockStage2 = stdx::make_unique<RouterStageMock>(); ClusterClientCursorParams params2(NamespaceString("test"), {}); - auto lsid = LogicalSessionId::gen(); + auto lsid = makeLogicalSessionIdForTest(); ClusterClientCursorImpl cursor2{std::move(mockStage2), std::move(params2), lsid}; ASSERT(*(cursor2.getLsid()) == lsid); } diff --git a/src/mongo/s/query/cluster_cursor_manager_test.cpp b/src/mongo/s/query/cluster_cursor_manager_test.cpp index 35bbf4ad847..46377d2f483 100644 --- a/src/mongo/s/query/cluster_cursor_manager_test.cpp +++ b/src/mongo/s/query/cluster_cursor_manager_test.cpp @@ -969,7 +969,7 @@ TEST_F(ClusterCursorManagerTest, CursorsWithoutSessions) { */ TEST_F(ClusterCursorManagerTest, OneCursorWithASession) { // Add a cursor with a session to the cursor manager. - auto lsid = LogicalSessionId::gen(); + auto lsid = makeLogicalSessionIdForTest(); auto cursorId = assertGet( getManager()->registerCursor(nullptr, allocateMockCursor(lsid), @@ -1003,7 +1003,7 @@ TEST_F(ClusterCursorManagerTest, OneCursorWithASession) { */ TEST_F(ClusterCursorManagerTest, GetSessionIdsWhileCheckedOut) { // Add a cursor with a session to the cursor manager. - auto lsid = LogicalSessionId::gen(); + auto lsid = makeLogicalSessionIdForTest(); auto cursorId = assertGet( getManager()->registerCursor(nullptr, allocateMockCursor(lsid), @@ -1024,7 +1024,7 @@ TEST_F(ClusterCursorManagerTest, GetSessionIdsWhileCheckedOut) { */ TEST_F(ClusterCursorManagerTest, MultipleCursorsWithSameSession) { // Add two cursors on the same session to the cursor manager. - auto lsid = LogicalSessionId::gen(); + auto lsid = makeLogicalSessionIdForTest(); auto cursorId1 = assertGet( getManager()->registerCursor(nullptr, allocateMockCursor(lsid), @@ -1039,7 +1039,7 @@ TEST_F(ClusterCursorManagerTest, MultipleCursorsWithSameSession) { ClusterCursorManager::CursorLifetime::Mortal)); // Retrieve all sessions - set should contain just lsid. - stdx::unordered_set<LogicalSessionId, LogicalSessionId::Hash> lsids; + stdx::unordered_set<LogicalSessionId, LogicalSessionIdHash> lsids; getManager()->appendActiveSessions(&lsids); ASSERT_EQ(lsids.size(), size_t(1)); ASSERT(lsids.find(lsid) != lsids.end()); @@ -1069,8 +1069,8 @@ TEST_F(ClusterCursorManagerTest, MultipleCursorsWithSameSession) { * Test a manager with multiple cursors running inside of different sessions. */ TEST_F(ClusterCursorManagerTest, MultipleCursorsMultipleSessions) { - auto lsid1 = LogicalSessionId::gen(); - auto lsid2 = LogicalSessionId::gen(); + auto lsid1 = makeLogicalSessionIdForTest(); + auto lsid2 = makeLogicalSessionIdForTest(); // Register two cursors with different lsids, and one without. CursorId cursor1 = assertGet( @@ -1116,7 +1116,7 @@ TEST_F(ClusterCursorManagerTest, MultipleCursorsMultipleSessions) { TEST_F(ClusterCursorManagerTest, ManyCursorsManySessions) { const int count = 10000; for (int i = 0; i < count; i++) { - auto lsid = LogicalSessionId::gen(); + auto lsid = makeLogicalSessionIdForTest(); ASSERT_OK( getManager()->registerCursor(nullptr, allocateMockCursor(lsid), diff --git a/src/mongo/shell/shell_utils.cpp b/src/mongo/shell/shell_utils.cpp index 4c1d70f6f03..d0189a75432 100644 --- a/src/mongo/shell/shell_utils.cpp +++ b/src/mongo/shell/shell_utils.cpp @@ -183,6 +183,33 @@ BSONObj validateIndexKey(const BSONObj& a, void* data) { return BSON("" << BSON("ok" << true)); } +BSONObj computeSHA256Block(const BSONObj& a, void* data) { + std::vector<ConstDataRange> blocks; + + auto ele = a[0]; + + BSONObjBuilder bob; + switch (ele.type()) { + case BinData: { + int len; + const char* ptr = ele.binData(len); + SHA256Block::computeHash({ConstDataRange(ptr, len)}).appendAsBinData(bob, ""_sd); + + break; + } + case String: { + auto str = ele.valueStringData(); + SHA256Block::computeHash({ConstDataRange(str.rawData(), str.size())}) + .appendAsBinData(bob, ""_sd); + break; + } + default: + uasserted(ErrorCodes::BadValue, "Can only computeSHA256Block of strings and bindata"); + } + + return bob.obj(); +} + BSONObj replMonitorStats(const BSONObj& a, void* data) { uassert(17134, "replMonitorStats requires a single string argument (the ReplSet name)", @@ -226,6 +253,7 @@ void installShellUtils(Scope& scope) { scope.injectNative("getBuildInfo", getBuildInfo); scope.injectNative("isKeyTooLarge", isKeyTooLarge); scope.injectNative("validateIndexKey", validateIndexKey); + scope.injectNative("computeSHA256Block", computeSHA256Block); #ifndef MONGO_SAFE_SHELL // can't launch programs diff --git a/src/mongo/util/uuid.h b/src/mongo/util/uuid.h index 74139dcdbbf..09a6751fc1f 100644 --- a/src/mongo/util/uuid.h +++ b/src/mongo/util/uuid.h @@ -54,7 +54,9 @@ class UUID { // Make the IDL generated parser a friend friend class One_UUID; - friend class Logical_session_id; + friend class LogicalSessionId; + friend class LogicalSessionToClient; + friend class LogicalSessionFromClient; friend class repl::OplogEntryBase; public: |