Skip to content

Commit edfe3f3

Browse files
committed
SERVER-30298 Add UserDigest LogicalSessionID
Inclusion of a sha256 digest of the full username to the logical session id (in addition to the current guid) is necessary to fully disambiguate logical sessions in degraded clusters (when the authoritative record for a session is unreachable). Semantics for the uid are as follows: session creation via startSession() * Sessions can only be created with one, and only one, user authenticated * The composite key is created from a guid created on the spot, as well as the digest of the currently auth'd username * Only the session guid is returned to the user * This prevents outside users from attempting to send back a value we'd have to check. It's preferable to decorate the guid with the user digest per command, rather than having to check a value the user might send. session use for a command * Sessions are passed via the lsid top level field in any command * Sessions are only meaningful for commands which requireAuth. For sessions which don't require auth, we strip session information from the command at parse time * Session ids are passed as an object, which can optionally include the username digest * It is illegal to pass the username digest unless the currently auth'd user has the impersonate privilege (the __system user does). This enables sessions on shard servers via mongos
1 parent cb36a96 commit edfe3f3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+796
-1238
lines changed

jstests/sharding/session_info_in_oplog.js

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,17 @@
77

88
var checkOplog = function(mainConn, priConn) {
99
var lsid = UUID();
10+
var uid = function() {
11+
var user = mainConn.getDB("admin")
12+
.runCommand({connectionStatus: 1})
13+
.authInfo.authenticatedUsers[0];
14+
15+
if (user) {
16+
return computeSHA256Block(user.user + "@" + user.db);
17+
} else {
18+
return computeSHA256Block("");
19+
}
20+
}();
1021

1122
////////////////////////////////////////////////////////////////////////
1223
// Test insert command
@@ -15,7 +26,7 @@
1526
insert: 'user',
1627
documents: [{_id: 10}, {_id: 30}],
1728
ordered: false,
18-
lsid: {id: {uuid: lsid}},
29+
lsid: {id: lsid},
1930
txnNumber: NumberLong(34),
2031
};
2132

@@ -26,14 +37,16 @@
2637
var firstDoc = oplog.findOne({ns: 'test.user', 'o._id': 10});
2738
assert(firstDoc != null);
2839
assert(firstDoc.lsid != null);
29-
assert.eq(lsid, firstDoc.lsid.id.uuid);
40+
assert.eq(lsid, firstDoc.lsid.id);
41+
assert.eq(uid, firstDoc.lsid.uid);
3042
assert.eq(NumberLong(34), firstDoc.txnNumber);
3143
assert.eq(0, firstDoc.stmtId);
3244

3345
var secondDoc = oplog.findOne({ns: 'test.user', 'o._id': 30});
3446
assert(secondDoc != null);
3547
assert(secondDoc.lsid != null);
36-
assert.eq(lsid, secondDoc.lsid.id.uuid);
48+
assert.eq(lsid, secondDoc.lsid.id);
49+
assert.eq(uid, firstDoc.lsid.uid);
3750
assert.eq(NumberLong(34), secondDoc.txnNumber);
3851
assert.eq(1, secondDoc.stmtId);
3952

@@ -48,7 +61,7 @@
4861
{q: {_id: 30}, u: {z: 1}} // replacement
4962
],
5063
ordered: false,
51-
lsid: {id: {uuid: lsid}},
64+
lsid: {id: lsid},
5265
txnNumber: NumberLong(35),
5366
};
5467

@@ -57,21 +70,24 @@
5770
firstDoc = oplog.findOne({ns: 'test.user', op: 'u', 'o2._id': 10});
5871
assert(firstDoc != null);
5972
assert(firstDoc.lsid != null);
60-
assert.eq(lsid, firstDoc.lsid.id.uuid);
73+
assert.eq(lsid, firstDoc.lsid.id);
74+
assert.eq(uid, firstDoc.lsid.uid);
6175
assert.eq(NumberLong(35), firstDoc.txnNumber);
6276
assert.eq(0, firstDoc.stmtId);
6377

6478
secondDoc = oplog.findOne({ns: 'test.user', op: 'i', 'o._id': 20});
6579
assert(secondDoc != null);
6680
assert(secondDoc.lsid != null);
67-
assert.eq(lsid, secondDoc.lsid.id.uuid);
81+
assert.eq(lsid, secondDoc.lsid.id);
82+
assert.eq(uid, firstDoc.lsid.uid);
6883
assert.eq(NumberLong(35), secondDoc.txnNumber);
6984
assert.eq(1, secondDoc.stmtId);
7085

7186
var thirdDoc = oplog.findOne({ns: 'test.user', op: 'u', 'o2._id': 30});
7287
assert(thirdDoc != null);
7388
assert(thirdDoc.lsid != null);
74-
assert.eq(lsid, thirdDoc.lsid.id.uuid);
89+
assert.eq(lsid, thirdDoc.lsid.id);
90+
assert.eq(uid, firstDoc.lsid.uid);
7591
assert.eq(NumberLong(35), thirdDoc.txnNumber);
7692
assert.eq(2, thirdDoc.stmtId);
7793

@@ -82,7 +98,7 @@
8298
delete: 'user',
8399
deletes: [{q: {_id: 10}, limit: 1}, {q: {_id: 20}, limit: 1}],
84100
ordered: false,
85-
lsid: {id: {uuid: lsid}},
101+
lsid: {id: lsid},
86102
txnNumber: NumberLong(36),
87103
};
88104

@@ -91,14 +107,16 @@
91107
firstDoc = oplog.findOne({ns: 'test.user', op: 'd', 'o._id': 10});
92108
assert(firstDoc != null);
93109
assert(firstDoc.lsid != null);
94-
assert.eq(lsid, firstDoc.lsid.id.uuid);
110+
assert.eq(lsid, firstDoc.lsid.id);
111+
assert.eq(uid, firstDoc.lsid.uid);
95112
assert.eq(NumberLong(36), firstDoc.txnNumber);
96113
assert.eq(0, firstDoc.stmtId);
97114

98115
secondDoc = oplog.findOne({ns: 'test.user', op: 'd', 'o._id': 20});
99116
assert(secondDoc != null);
100117
assert(secondDoc.lsid != null);
101-
assert.eq(lsid, secondDoc.lsid.id.uuid);
118+
assert.eq(lsid, secondDoc.lsid.id);
119+
assert.eq(uid, firstDoc.lsid.uid);
102120
assert.eq(NumberLong(36), secondDoc.txnNumber);
103121
assert.eq(1, secondDoc.stmtId);
104122
};

src/mongo/SConscript

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,7 @@ if not has_option('noshell') and usemozjs:
411411
"shell/shell_utils_launcher.cpp",
412412
],
413413
LIBDEPS=[
414+
'db/logical_session_id_helpers',
414415
'db/catalog/index_key_validate',
415416
'db/index/external_key_generator',
416417
'db/query/command_request_response',

src/mongo/db/SConscript

Lines changed: 16 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ env.CppUnitTest(
206206
],
207207
LIBDEPS=[
208208
'logical_session_id',
209+
'logical_session_id_helpers',
209210
'service_context',
210211
'$BUILD_DIR/mongo/db/auth/authorization_manager_mock_init',
211212
'$BUILD_DIR/mongo/db/service_context_noop_init',
@@ -872,74 +873,41 @@ env.Library(
872873
'logical_session_id.cpp',
873874
env.Idlc('logical_session_id.idl')[0],
874875
],
875-
LIBDEPS=[
876-
'$BUILD_DIR/mongo/idl/idl_parser',
877-
'$BUILD_DIR/mongo/util/uuid',
878-
'$BUILD_DIR/mongo/base',
879-
],
880-
)
881-
882-
env.CppUnitTest(
883-
target='logical_session_id_test',
884-
source=[
885-
'logical_session_id_test.cpp',
886-
],
887-
LIBDEPS=[
888-
'$BUILD_DIR/mongo/base',
889-
'logical_session_id'
890-
],
891-
)
892-
893-
env.Library(
894-
target='signed_logical_session_id',
895-
source=[
896-
'signed_logical_session_id.cpp',
897-
env.Idlc('signed_logical_session_id.idl')[0],
898-
],
899876
LIBDEPS=[
900877
'$BUILD_DIR/mongo/base',
901-
'$BUILD_DIR/mongo/crypto/sha1_block',
878+
'$BUILD_DIR/mongo/crypto/sha256_block',
879+
'$BUILD_DIR/mongo/crypto/sha_block_${MONGO_CRYPTO}',
902880
'$BUILD_DIR/mongo/idl/idl_parser',
903881
'$BUILD_DIR/mongo/util/uuid',
904-
'logical_session_id'
905-
],
906-
)
907-
908-
env.CppUnitTest(
909-
target='signed_logical_session_id_test',
910-
source=[
911-
'signed_logical_session_id_test.cpp',
912-
],
913-
LIBDEPS=[
914-
'$BUILD_DIR/mongo/base',
915-
'signed_logical_session_id',
916882
],
917883
)
918884

919885
env.Library(
920-
target='logical_session_record',
886+
target='logical_session_id_helpers',
921887
source=[
922-
'logical_session_record.cpp',
923-
env.Idlc('logical_session_record.idl')[0],
888+
'logical_session_id_helpers.cpp',
924889
],
925890
LIBDEPS=[
926-
'$BUILD_DIR/mongo/base',
927-
'$BUILD_DIR/mongo/db/auth/user_name',
928-
'$BUILD_DIR/mongo/idl/idl_parser',
929891
'logical_session_id',
930-
'signed_logical_session_id',
892+
'logical_session_cache',
893+
'$BUILD_DIR/mongo/db/auth/authcore',
931894
],
932895
)
933896

934897
env.CppUnitTest(
935-
target='logical_session_record_test',
898+
target='logical_session_id_test',
936899
source=[
937-
'logical_session_record_test.cpp',
900+
'logical_session_id_test.cpp',
938901
],
939902
LIBDEPS=[
940903
'$BUILD_DIR/mongo/base',
904+
'$BUILD_DIR/mongo/db/service_context_noop_init',
905+
'$BUILD_DIR/mongo/transport/transport_layer_mock',
906+
'$BUILD_DIR/mongo/db/auth/authcore',
907+
'$BUILD_DIR/mongo/db/auth/authmocks',
908+
'$BUILD_DIR/mongo/db/auth/authorization_session_for_test',
941909
'logical_session_id',
942-
'logical_session_record',
910+
'logical_session_id_helpers',
943911
],
944912
)
945913

@@ -954,7 +922,6 @@ env.Library(
954922
'keys_collection_manager',
955923
'keys_collection_manager_zero',
956924
'logical_clock',
957-
'signed_logical_session_id',
958925
],
959926
)
960927

@@ -1032,7 +999,6 @@ env.Library(
1032999
],
10331000
LIBDEPS=[
10341001
'logical_session_id',
1035-
'logical_session_record',
10361002
'sessions_collection',
10371003
'server_parameters',
10381004
'service_liason',
@@ -1050,10 +1016,10 @@ envWithAsio.CppUnitTest(
10501016
'keys_collection_manager',
10511017
'keys_collection_document',
10521018
'logical_clock',
1019+
'logical_session_id_helpers',
10531020
'logical_session_cache',
10541021
'service_liason_mock',
10551022
'sessions_collection_mock',
1056-
'signed_logical_session_id',
10571023
],
10581024
)
10591025

src/mongo/db/auth/sasl_commands.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ class CmdSaslStart : public BasicCommand {
8585
virtual bool slaveOk() const {
8686
return true;
8787
}
88-
virtual bool requiresAuth() {
88+
bool requiresAuth() const override {
8989
return false;
9090
}
9191
};
@@ -111,7 +111,7 @@ class CmdSaslContinue : public BasicCommand {
111111
virtual bool slaveOk() const {
112112
return true;
113113
}
114-
virtual bool requiresAuth() {
114+
bool requiresAuth() const override {
115115
return false;
116116
}
117117
};

src/mongo/db/auth/user.cpp

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,22 @@
4141

4242
namespace mongo {
4343

44-
User::User(const UserName& name) : _name(name), _id(), _refCount(0), _isValid(1) {}
44+
namespace {
45+
46+
SHA256Block computeDigest(const UserName& name, const boost::optional<OID>& id) {
47+
const auto& fn = name.getFullName();
48+
49+
if (id) {
50+
return SHA256Block::computeHash({id->toCDR(), ConstDataRange(fn.c_str(), fn.size())});
51+
} else {
52+
return SHA256Block::computeHash({ConstDataRange(fn.c_str(), fn.size())});
53+
}
54+
};
55+
56+
} // namespace
57+
58+
User::User(const UserName& name)
59+
: _name(name), _id(), _digest(computeDigest(_name, _id)), _refCount(0), _isValid(1) {}
4560

4661
User::~User() {
4762
dassert(_refCount == 0);
@@ -55,6 +70,10 @@ const boost::optional<OID>& User::getID() const {
5570
return _id;
5671
}
5772

73+
const SHA256Block& User::getDigest() const {
74+
return _digest;
75+
}
76+
5877
RoleNameIterator User::getRoles() const {
5978
return makeRoleNameIteratorForContainer(_roles);
6079
}
@@ -87,17 +106,9 @@ const ActionSet User::getActionsForResource(const ResourcePattern& resource) con
87106
return it->second.getActions();
88107
}
89108

90-
User* User::clone() const {
91-
std::unique_ptr<User> result(new User(_name));
92-
result->_id = _id;
93-
result->_privileges = _privileges;
94-
result->_roles = _roles;
95-
result->_credentials = _credentials;
96-
return result.release();
97-
}
98-
99109
void User::setID(boost::optional<OID> id) {
100110
_id = std::move(id);
111+
_digest = computeDigest(_name, _id);
101112
}
102113

103114
void User::setCredentials(const CredentialData& credentials) {

src/mongo/db/auth/user.h

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232

3333
#include "mongo/base/disallow_copying.h"
3434
#include "mongo/bson/oid.h"
35+
#include "mongo/crypto/sha256_block.h"
3536
#include "mongo/db/auth/privilege.h"
3637
#include "mongo/db/auth/resource_pattern.h"
3738
#include "mongo/db/auth/restriction_set.h"
@@ -92,6 +93,11 @@ class User {
9293
*/
9394
const boost::optional<OID>& getID() const;
9495

96+
/**
97+
* Returns a digest of the user's identity
98+
*/
99+
const SHA256Block& getDigest() const;
100+
95101
/**
96102
* Returns an iterator over the names of the user's direct roles
97103
*/
@@ -137,11 +143,6 @@ class User {
137143
*/
138144
uint32_t getRefCount() const;
139145

140-
/**
141-
* Clones this user into a new, valid User object with refcount of 0.
142-
*/
143-
User* clone() const;
144-
145146
// Mutators below. Mutation functions should *only* be called by the AuthorizationManager
146147

147148
/**
@@ -237,6 +238,9 @@ class User {
237238
// an unset _id field to be a distinct value that will fail to compare to any other id value.
238239
boost::optional<OID> _id;
239240

241+
// Digest of the full username
242+
SHA256Block _digest;
243+
240244
// Maps resource name to privilege on that resource
241245
ResourcePrivilegeMap _privileges;
242246

src/mongo/db/commands.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,11 @@ class CommandInterface {
147147
*/
148148
virtual bool shouldAffectCommandCounter() const = 0;
149149

150+
/**
151+
* Return true if the command requires auth.
152+
*/
153+
virtual bool requiresAuth() const = 0;
154+
150155
/**
151156
* Generates help text for this command.
152157
*/
@@ -302,6 +307,10 @@ class Command : public CommandInterface {
302307
return true;
303308
}
304309

310+
bool requiresAuth() const override {
311+
return true;
312+
}
313+
305314
void help(std::stringstream& help) const override;
306315

307316
Status explain(OperationContext* opCtx,

src/mongo/db/commands/SConscript

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,8 @@ env.Library(
8585
'$BUILD_DIR/mongo/db/lasterror',
8686
'$BUILD_DIR/mongo/db/log_process_details',
8787
'$BUILD_DIR/mongo/db/logical_session_cache',
88-
'$BUILD_DIR/mongo/db/logical_session_record',
88+
'$BUILD_DIR/mongo/db/logical_session_id',
89+
'$BUILD_DIR/mongo/db/logical_session_id_helpers',
8990
'$BUILD_DIR/mongo/db/matcher/expressions',
9091
'$BUILD_DIR/mongo/db/repl/isself',
9192
'$BUILD_DIR/mongo/db/repl/repl_coordinator_global',

src/mongo/db/commands/shutdown.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class CmdShutdown : public BasicCommand {
3939
public:
4040
CmdShutdown() : BasicCommand("shutdown") {}
4141

42-
virtual bool requiresAuth() {
42+
bool requiresAuth() const override {
4343
return true;
4444
}
4545
virtual bool adminOnly() const {

0 commit comments

Comments
 (0)