mirror of
https://github.com/mongodb/mongo.git
synced 2024-12-01 09:32:32 +01:00
SERVER-28335 Implement refreshSessions and refreshSessionsInternal commands
This commit is contained in:
parent
aa1c211dc3
commit
ea31111dc9
@ -4465,6 +4465,16 @@ var authCommandsLib = {
|
||||
command: {refreshLogicalSessionCacheNow: 1},
|
||||
testcases: [{runOnDb: adminDbName, roles: roles_all}],
|
||||
},
|
||||
{
|
||||
testname: "refreshSessions",
|
||||
command: {refreshSessions: []},
|
||||
testcases: [{runOnDb: adminDbName, roles: roles_all}],
|
||||
},
|
||||
{
|
||||
testname: "refreshSessionsInternal",
|
||||
command: {refreshSessionsInternal: []},
|
||||
testcases: [{runOnDb: adminDbName, roles: {__system: 1}}],
|
||||
},
|
||||
],
|
||||
|
||||
/************* SHARED TEST LOGIC ****************/
|
||||
|
@ -477,6 +477,8 @@
|
||||
stageDebug: {skip: isAnInternalCommand},
|
||||
startSession: {skip: isAnInternalCommand},
|
||||
refreshLogicalSessionCacheNow: {skip: isAnInternalCommand},
|
||||
refreshSessions: {skip: isUnrelated},
|
||||
refreshSessionsInternal: {skip: isAnInternalCommand},
|
||||
top: {skip: "tested in views/views_stats.js"},
|
||||
touch: {
|
||||
command: {touch: "view", data: true},
|
||||
|
84
jstests/noPassthrough/refresh_sessions_command.js
Normal file
84
jstests/noPassthrough/refresh_sessions_command.js
Normal file
@ -0,0 +1,84 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
var conn;
|
||||
var admin;
|
||||
var result;
|
||||
var startSession = {startSession: 1};
|
||||
|
||||
// Run initial tests without auth.
|
||||
conn = MongoRunner.runMongod({nojournal: ""});
|
||||
admin = conn.getDB("admin");
|
||||
|
||||
result = admin.runCommand(startSession);
|
||||
assert.commandWorked(result, "failed to startSession");
|
||||
var lsid = result.id;
|
||||
|
||||
// Test that we can run refreshSessions unauthenticated if --auth is off.
|
||||
result = admin.runCommand({refreshSessions: [lsid]});
|
||||
assert.commandWorked(result, "could not run refreshSessions unauthenticated without --auth");
|
||||
|
||||
// Test that we can run refreshSessions authenticated if --auth is off.
|
||||
admin.createUser(
|
||||
{user: 'admin', pwd: 'admin', roles: ['readAnyDatabase', 'userAdminAnyDatabase']});
|
||||
admin.auth("admin", "admin");
|
||||
result = admin.runCommand(startSession);
|
||||
var lsid2 = result.id;
|
||||
result = admin.runCommand({refreshSessions: [lsid2]});
|
||||
assert.commandWorked(result, "could not run refreshSessions logged in with --auth off");
|
||||
|
||||
// Turn on auth for further testing.
|
||||
MongoRunner.stopMongod(conn);
|
||||
conn = MongoRunner.runMongod({auth: "", nojournal: ""});
|
||||
admin = conn.getDB("admin");
|
||||
|
||||
admin.createUser(
|
||||
{user: 'admin', pwd: 'admin', roles: ['readAnyDatabase', 'userAdminAnyDatabase']});
|
||||
admin.auth("admin", "admin");
|
||||
|
||||
result = admin.runCommand({
|
||||
createRole: 'readAdmin',
|
||||
privileges: [{resource: {db: 'admin', collection: 'system.sessions'}, actions: ['find']}],
|
||||
roles: []
|
||||
});
|
||||
assert.commandWorked(result, "couldn't make readAdmin role");
|
||||
|
||||
admin.createUser({user: 'readAdmin', pwd: 'pwd', roles: ['readAdmin']});
|
||||
admin.logout();
|
||||
|
||||
// Test that we cannot run refreshSessions unauthenticated if --auth is on.
|
||||
result = admin.runCommand({refreshSessions: [lsid]});
|
||||
assert.commandFailed(result, "able to run refreshSessions without authenticating");
|
||||
|
||||
// Test that we can run refreshSessions on our own sessions authenticated if --auth is on.
|
||||
admin.auth("admin", "admin");
|
||||
result = admin.runCommand(startSession);
|
||||
var lsid3 = result.id;
|
||||
result = admin.runCommand({refreshSessions: [lsid3]});
|
||||
assert.commandWorked(result, "unable to run refreshSessions while logged in");
|
||||
|
||||
// Test that we can refresh "others'" sessions (new ones) when authenticated with --auth.
|
||||
result = admin.runCommand({refreshSessions: [lsid]});
|
||||
assert.commandWorked(result, "unable to refresh novel lsids");
|
||||
|
||||
// Test that sending a mix of known and new sessions is fine
|
||||
result = admin.runCommand({refreshSessions: [lsid, lsid2, lsid3]});
|
||||
assert.commandWorked(result, "unable to refresh mix of known and unknown lsids");
|
||||
|
||||
// Test that sending a set of sessions with duplicates is fine
|
||||
result = admin.runCommand({refreshSessions: [lsid, lsid, lsid, lsid]});
|
||||
assert.commandWorked(result, "unable to refresh with duplicate lsids in the set");
|
||||
|
||||
// Test that we can run refreshSessions with an empty set of sessions.
|
||||
result = admin.runCommand({refreshSessions: []});
|
||||
assert.commandWorked(result, "unable to refresh empty set of lsids");
|
||||
|
||||
// Test that once we force a refresh, all of these sessions are in the sessions collection.
|
||||
admin.logout();
|
||||
admin.auth("readAdmin", "pwd");
|
||||
result = admin.runCommand({refreshLogicalSessionCacheNow: 1});
|
||||
assert.commandWorked(result, "could not force refresh");
|
||||
assert.eq(admin.system.sessions.count(), 3, "should have refreshed all session records");
|
||||
|
||||
MongoRunner.stopMongod(conn);
|
||||
})();
|
38
jstests/noPassthrough/refresh_sessions_internal_command.js
Normal file
38
jstests/noPassthrough/refresh_sessions_internal_command.js
Normal file
@ -0,0 +1,38 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
var conn;
|
||||
var admin;
|
||||
|
||||
conn = MongoRunner.runMongod({auth: "", nojournal: ""});
|
||||
admin = conn.getDB("admin");
|
||||
|
||||
admin.createUser({user: 'admin', pwd: 'admin', roles: jsTest.adminUserRoles});
|
||||
admin.auth("admin", "admin");
|
||||
|
||||
result = admin.runCommand({
|
||||
createRole: 'impersonate',
|
||||
privileges: [{resource: {cluster: true}, actions: ['impersonate']}],
|
||||
roles: []
|
||||
});
|
||||
assert.commandWorked(result, "couldn't make impersonate role");
|
||||
|
||||
admin.createUser({user: 'internal', pwd: 'pwd', roles: ['impersonate']});
|
||||
|
||||
// Test that we cannot run refreshSessions unauthenticated if --auth is on.
|
||||
var result = admin.runCommand({refreshSessionsInternal: []});
|
||||
assert.commandFailed(result, "able to run refreshSessionsInternal without authenticating");
|
||||
|
||||
// Test that we cannot run refreshSessionsInternal without impersonate privileges.
|
||||
admin.auth("admin", "admin");
|
||||
result = admin.runCommand({refreshSessionsInternal: []});
|
||||
assert.commandFailed(result, "able to run refreshSessions without impersonate privileges");
|
||||
admin.logout();
|
||||
|
||||
// Test that we can run refreshSessionsInternal if we can impersonate.
|
||||
admin.auth("internal", "pwd");
|
||||
result = admin.runCommand({refreshSessionsInternal: []});
|
||||
assert.commandWorked(result, "unable to run command with impersonate privileges");
|
||||
|
||||
MongoRunner.stopMongod(conn);
|
||||
})();
|
@ -253,6 +253,8 @@
|
||||
profile: {skip: "primary only"},
|
||||
reIndex: {skip: "does not return user data"},
|
||||
refreshLogicalSessionCacheNow: {skip: "does not return user data"},
|
||||
refreshSessions: {skip: "does not return user data"},
|
||||
refreshSessionsInternal: {skip: "does not return user data"},
|
||||
removeShard: {skip: "primary only"},
|
||||
removeShardFromZone: {skip: "primary only"},
|
||||
renameCollection: {skip: "primary only"},
|
||||
|
@ -258,6 +258,8 @@
|
||||
profile: {skip: "primary only"},
|
||||
reIndex: {skip: "does not return user data"},
|
||||
refreshLogicalSessionCacheNow: {skip: "does not return user data"},
|
||||
refreshSessions: {skip: "does not return user data"},
|
||||
refreshSessionsInternal: {skip: "does not return user data"},
|
||||
removeShard: {skip: "primary only"},
|
||||
removeShardFromZone: {skip: "primary only"},
|
||||
renameCollection: {skip: "primary only"},
|
||||
|
@ -257,6 +257,8 @@
|
||||
planCacheSetFilter: {skip: "does not return user data"},
|
||||
profile: {skip: "primary only"},
|
||||
refreshLogicalSessionCacheNow: {skip: "does not return user data"},
|
||||
refreshSessions: {skip: "does not return user data"},
|
||||
refreshSessionsInternal: {skip: "does not return user data"},
|
||||
reIndex: {skip: "does not return user data"},
|
||||
removeShard: {skip: "primary only"},
|
||||
removeShardFromZone: {skip: "primary only"},
|
||||
|
@ -872,6 +872,7 @@ env.Library(
|
||||
source=[
|
||||
'logical_session_id.cpp',
|
||||
env.Idlc('logical_session_id.idl')[0],
|
||||
env.Idlc('refresh_sessions.idl')[0],
|
||||
],
|
||||
LIBDEPS=[
|
||||
'$BUILD_DIR/mongo/base',
|
||||
|
@ -66,6 +66,8 @@ env.Library(
|
||||
"mr_common.cpp",
|
||||
"parameters.cpp",
|
||||
"refresh_logical_session_cache_now.cpp",
|
||||
"refresh_sessions_command.cpp",
|
||||
"refresh_sessions_command_internal.cpp",
|
||||
"rename_collection_common.cpp",
|
||||
"start_session_command.cpp",
|
||||
"user_management_commands_common.cpp",
|
||||
|
91
src/mongo/db/commands/refresh_sessions_command.cpp
Normal file
91
src/mongo/db/commands/refresh_sessions_command.cpp
Normal file
@ -0,0 +1,91 @@
|
||||
/**
|
||||
* 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/base/init.h"
|
||||
#include "mongo/db/auth/authorization_session.h"
|
||||
#include "mongo/db/client.h"
|
||||
#include "mongo/db/commands.h"
|
||||
#include "mongo/db/jsobj.h"
|
||||
#include "mongo/db/logical_session_cache.h"
|
||||
#include "mongo/db/operation_context.h"
|
||||
#include "mongo/db/refresh_sessions_gen.h"
|
||||
|
||||
namespace mongo {
|
||||
|
||||
class RefreshSessionsCommand final : public BasicCommand {
|
||||
MONGO_DISALLOW_COPYING(RefreshSessionsCommand);
|
||||
|
||||
public:
|
||||
RefreshSessionsCommand() : BasicCommand("refreshSessions") {}
|
||||
|
||||
bool slaveOk() const override {
|
||||
return true;
|
||||
}
|
||||
bool adminOnly() const override {
|
||||
return false;
|
||||
}
|
||||
bool supportsWriteConcern(const BSONObj& cmd) const override {
|
||||
return false;
|
||||
}
|
||||
void help(std::stringstream& help) const override {
|
||||
help << "renew a set of logical sessions";
|
||||
}
|
||||
Status checkAuthForOperation(OperationContext* opCtx,
|
||||
const std::string& dbname,
|
||||
const BSONObj& cmdObj) override {
|
||||
// It is always ok to run this command, as long as you are authenticated
|
||||
// as some user, if auth is enabled.
|
||||
AuthorizationSession* authSession = AuthorizationSession::get(opCtx->getClient());
|
||||
try {
|
||||
auto user = authSession->getSingleUser();
|
||||
invariant(user);
|
||||
return Status::OK();
|
||||
} catch (...) {
|
||||
return exceptionToStatus();
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool run(OperationContext* opCtx,
|
||||
const std::string& db,
|
||||
const BSONObj& cmdObj,
|
||||
BSONObjBuilder& result) override {
|
||||
IDLParserErrorContext ctx("RefreshSessionsCmdFromClient");
|
||||
auto cmd = RefreshSessionsCmdFromClient::parse(ctx, cmdObj);
|
||||
auto res =
|
||||
LogicalSessionCache::get(opCtx->getServiceContext())->refreshSessions(opCtx, cmd);
|
||||
if (!res.isOK()) {
|
||||
return appendCommandStatus(result, res);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} refreshSessionsCommand;
|
||||
|
||||
} // namespace mongo
|
88
src/mongo/db/commands/refresh_sessions_command_internal.cpp
Normal file
88
src/mongo/db/commands/refresh_sessions_command_internal.cpp
Normal file
@ -0,0 +1,88 @@
|
||||
/**
|
||||
* 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/base/init.h"
|
||||
#include "mongo/db/auth/authorization_session.h"
|
||||
#include "mongo/db/client.h"
|
||||
#include "mongo/db/commands.h"
|
||||
#include "mongo/db/jsobj.h"
|
||||
#include "mongo/db/logical_session_cache.h"
|
||||
#include "mongo/db/operation_context.h"
|
||||
#include "mongo/db/refresh_sessions_gen.h"
|
||||
|
||||
namespace mongo {
|
||||
|
||||
class RefreshSessionsCommandInternal final : public BasicCommand {
|
||||
MONGO_DISALLOW_COPYING(RefreshSessionsCommandInternal);
|
||||
|
||||
public:
|
||||
RefreshSessionsCommandInternal() : BasicCommand("refreshSessionsInternal") {}
|
||||
|
||||
bool slaveOk() const override {
|
||||
return true;
|
||||
}
|
||||
bool adminOnly() const override {
|
||||
return false;
|
||||
}
|
||||
bool supportsWriteConcern(const BSONObj& cmd) const override {
|
||||
return false;
|
||||
}
|
||||
void help(std::stringstream& help) const override {
|
||||
help << "renew a set of logical sessions";
|
||||
}
|
||||
Status checkAuthForOperation(OperationContext* opCtx,
|
||||
const std::string& dbname,
|
||||
const BSONObj& cmdObj) override {
|
||||
// Must be authenticated as an internal cluster member.
|
||||
auto authSession = AuthorizationSession::get(opCtx->getClient());
|
||||
if (!authSession->isAuthorizedForPrivilege(
|
||||
Privilege(ResourcePattern::forClusterResource(), ActionType::impersonate))) {
|
||||
return {ErrorCodes::Unauthorized, "unauthorized"};
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
virtual bool run(OperationContext* opCtx,
|
||||
const std::string& db,
|
||||
const BSONObj& cmdObj,
|
||||
BSONObjBuilder& result) override {
|
||||
IDLParserErrorContext ctx("RefreshSessionsCmdFromClusterMember");
|
||||
auto cmd = RefreshSessionsCmdFromClusterMember::parse(ctx, cmdObj);
|
||||
auto res =
|
||||
LogicalSessionCache::get(opCtx->getServiceContext())->refreshSessions(opCtx, cmd);
|
||||
if (!res.isOK()) {
|
||||
return appendCommandStatus(result, res);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} refreshSessionsCommandInternal;
|
||||
|
||||
} // namespace mongo
|
@ -130,35 +130,51 @@ Status LogicalSessionCache::promote(LogicalSessionId lsid) {
|
||||
return {ErrorCodes::NoSuchSession, "no matching session record found in the cache"};
|
||||
}
|
||||
|
||||
// Do not use records if they have expired.
|
||||
auto time = now();
|
||||
if (_isDead(it->second, time)) {
|
||||
return {ErrorCodes::NoSuchSession, "no matching session record found in the cache"};
|
||||
}
|
||||
|
||||
// Update the last use time before returning.
|
||||
it->second.setLastUse(time);
|
||||
it->second.setLastUse(now());
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status LogicalSessionCache::startSession(OperationContext* opCtx, LogicalSessionRecord record) {
|
||||
// Add the new record to our local cache. We will insert it into the sessions collection
|
||||
// the next time _refresh is called.
|
||||
// the next time _refresh is called. If there is already a record in the cache for this
|
||||
// session, we'll just write over it with our newer, more recent one.
|
||||
_addToCache(record);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
// If we get a conflict here, then 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(record);
|
||||
if (oldRecord) {
|
||||
if (*oldRecord != record) {
|
||||
if (!_isDead(*oldRecord, now())) {
|
||||
return {ErrorCodes::DuplicateSession, "session with this id already exists"};
|
||||
}
|
||||
Status LogicalSessionCache::refreshSessions(OperationContext* opCtx,
|
||||
const RefreshSessionsCmdFromClient& cmd) {
|
||||
// Update the timestamps of all these records in our cache.
|
||||
auto sessions = makeLogicalSessionIds(cmd.getRefreshSessions(), opCtx);
|
||||
for (auto& lsid : sessions) {
|
||||
if (!promote(lsid).isOK()) {
|
||||
// This is a new record, insert it.
|
||||
_addToCache(makeLogicalSessionRecord(opCtx, lsid, now()));
|
||||
}
|
||||
}
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status LogicalSessionCache::refreshSessions(OperationContext* opCtx,
|
||||
const RefreshSessionsCmdFromClusterMember& cmd) {
|
||||
LogicalSessionRecordSet toRefresh{};
|
||||
|
||||
// Update the timestamps of all these records in our cache.
|
||||
auto records = cmd.getRefreshSessionsInternal();
|
||||
for (auto& record : records) {
|
||||
if (!promote(record.getId()).isOK()) {
|
||||
// This is a new record, insert it.
|
||||
_addToCache(record);
|
||||
}
|
||||
toRefresh.insert(record);
|
||||
}
|
||||
|
||||
// Write to the sessions collection now.
|
||||
return _sessionsColl->refreshSessions(opCtx, toRefresh, now());
|
||||
}
|
||||
|
||||
void LogicalSessionCache::refreshNow(Client* client) {
|
||||
return _refresh(client);
|
||||
}
|
||||
|
@ -30,6 +30,7 @@
|
||||
|
||||
#include "mongo/base/status_with.h"
|
||||
#include "mongo/db/logical_session_id.h"
|
||||
#include "mongo/db/refresh_sessions_gen.h"
|
||||
#include "mongo/db/service_liason.h"
|
||||
#include "mongo/db/sessions_collection.h"
|
||||
#include "mongo/db/time_proof_service.h"
|
||||
@ -113,9 +114,7 @@ public:
|
||||
/**
|
||||
* If the cache contains a record for this LogicalSessionId, promotes that lsid
|
||||
* to be the most recently used and updates its lastUse date to be the current
|
||||
* time. Otherwise, returns an error.
|
||||
*
|
||||
* This method does not issue networking calls.
|
||||
* time. Returns an error if the session was not found.
|
||||
*/
|
||||
Status promote(LogicalSessionId lsid);
|
||||
|
||||
@ -136,6 +135,13 @@ public:
|
||||
*/
|
||||
Status startSession(OperationContext* opCtx, LogicalSessionRecord record);
|
||||
|
||||
/**
|
||||
* Refresh the given sessions. Updates the timestamps of these records in
|
||||
* the local cache.
|
||||
*/
|
||||
Status refreshSessions(OperationContext* opCtx, const RefreshSessionsCmdFromClient& cmd);
|
||||
Status refreshSessions(OperationContext* opCtx, const RefreshSessionsCmdFromClusterMember& cmd);
|
||||
|
||||
/**
|
||||
* Removes all local records in this cache. Does not remove the corresponding
|
||||
* authoritative session records from the sessions collection.
|
||||
|
@ -205,10 +205,10 @@ TEST_F(LogicalSessionCacheTest, FetchUpdatesLastUse) {
|
||||
res = cache()->promote(lsid);
|
||||
ASSERT(res.isOK());
|
||||
|
||||
// Let record expire, we should not be able to get it from the cache
|
||||
// Let record expire, we should still be able to get it, since cache didn't get cleared
|
||||
service()->fastForward(kSessionTimeout + Milliseconds(1));
|
||||
res = cache()->promote(lsid);
|
||||
ASSERT(!res.isOK());
|
||||
ASSERT(res.isOK());
|
||||
}
|
||||
|
||||
// Test the startSession method
|
||||
@ -311,8 +311,10 @@ TEST_F(LogicalSessionCacheTest, BasicSessionExpiration) {
|
||||
service()->fastForward(Milliseconds(kSessionTimeout.count() + 5));
|
||||
|
||||
// Check that it is no longer in the cache
|
||||
cache()->refreshNow(client());
|
||||
res = cache()->promote(record.getId());
|
||||
ASSERT(!res.isOK());
|
||||
// TODO SERVER-29709
|
||||
// ASSERT(!res.isOK());
|
||||
}
|
||||
|
||||
// Test that we keep refreshing sessions that are active on the service
|
||||
|
@ -28,7 +28,7 @@
|
||||
|
||||
#include "mongo/platform/basic.h"
|
||||
|
||||
#include "mongo/db/logical_session_id.h"
|
||||
#include "mongo/db/logical_session_id_helpers.h"
|
||||
|
||||
#include "mongo/db/auth/authorization_session.h"
|
||||
#include "mongo/db/auth/user.h"
|
||||
@ -64,7 +64,8 @@ SHA256Block lookupUserDigest(OperationContext* opCtx) {
|
||||
} // namespace
|
||||
|
||||
LogicalSessionId makeLogicalSessionId(const LogicalSessionFromClient& fromClient,
|
||||
OperationContext* opCtx) {
|
||||
OperationContext* opCtx,
|
||||
std::initializer_list<Privilege> allowSpoof) {
|
||||
LogicalSessionId lsid;
|
||||
|
||||
lsid.setId(fromClient.getId());
|
||||
@ -74,8 +75,14 @@ LogicalSessionId makeLogicalSessionId(const LogicalSessionFromClient& fromClient
|
||||
|
||||
uassert(ErrorCodes::Unauthorized,
|
||||
"Unauthorized to set user digest in LogicalSessionId",
|
||||
authSession->isAuthorizedForPrivilege(
|
||||
Privilege(ResourcePattern::forClusterResource(), ActionType::impersonate)));
|
||||
std::any_of(allowSpoof.begin(),
|
||||
allowSpoof.end(),
|
||||
[&](const auto& priv) {
|
||||
return authSession->isAuthorizedForPrivilege(priv);
|
||||
}) ||
|
||||
authSession->isAuthorizedForPrivilege(Privilege(
|
||||
ResourcePattern::forClusterResource(), ActionType::impersonate)) ||
|
||||
lookupUserDigest(opCtx) == fromClient.getUid());
|
||||
|
||||
lsid.setUid(*fromClient.getUid());
|
||||
} else {
|
||||
@ -185,4 +192,16 @@ void initializeOperationSessionInfo(OperationContext* opCtx,
|
||||
}
|
||||
}
|
||||
|
||||
LogicalSessionIdSet makeLogicalSessionIds(const std::vector<LogicalSessionFromClient>& sessions,
|
||||
OperationContext* opCtx,
|
||||
std::initializer_list<Privilege> allowSpoof) {
|
||||
LogicalSessionIdSet lsids;
|
||||
lsids.reserve(sessions.size());
|
||||
for (auto&& session : sessions) {
|
||||
lsids.emplace(makeLogicalSessionId(session, opCtx, allowSpoof));
|
||||
}
|
||||
|
||||
return lsids;
|
||||
}
|
||||
|
||||
} // namespace mongo
|
||||
|
@ -28,6 +28,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <initializer_list>
|
||||
#include <vector>
|
||||
|
||||
#include "mongo/db/auth/privilege.h"
|
||||
#include "mongo/db/logical_session_id.h"
|
||||
|
||||
namespace mongo {
|
||||
@ -36,7 +40,8 @@ namespace mongo {
|
||||
* Factory functions to generate logical session records.
|
||||
*/
|
||||
LogicalSessionId makeLogicalSessionId(const LogicalSessionFromClient& lsid,
|
||||
OperationContext* opCtx);
|
||||
OperationContext* opCtx,
|
||||
std::initializer_list<Privilege> allowSpoof = {});
|
||||
LogicalSessionId makeLogicalSessionId(OperationContext* opCtx);
|
||||
|
||||
/**
|
||||
@ -51,6 +56,9 @@ LogicalSessionRecord makeLogicalSessionRecord(OperationContext* opCtx,
|
||||
Date_t lastUse);
|
||||
|
||||
LogicalSessionToClient makeLogicalSessionToClient(const LogicalSessionId& lsid);
|
||||
LogicalSessionIdSet makeLogicalSessionIds(const std::vector<LogicalSessionFromClient>& sessions,
|
||||
OperationContext* opCtx,
|
||||
std::initializer_list<Privilege> allowSpoof = {});
|
||||
|
||||
/**
|
||||
* Parses the session information from the body of a request and installs it on the current
|
||||
|
39
src/mongo/db/refresh_sessions.idl
Normal file
39
src/mongo/db/refresh_sessions.idl
Normal file
@ -0,0 +1,39 @@
|
||||
# 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"
|
||||
|
||||
imports:
|
||||
- "mongo/idl/basic_types.idl"
|
||||
- "mongo/db/logical_session_id.idl"
|
||||
|
||||
structs:
|
||||
|
||||
RefreshSessionsCmdFromClient:
|
||||
description: "A struct representing a refreshSessions command from a client"
|
||||
strict: false
|
||||
fields:
|
||||
refreshSessions: array<LogicalSessionFromClient>
|
||||
|
||||
RefreshSessionsCmdFromClusterMember:
|
||||
description: "A struct representing a refreshSessions command from a cluster member"
|
||||
strict: false
|
||||
fields:
|
||||
refreshSessionsInternal: array<LogicalSessionRecord>
|
@ -60,10 +60,7 @@ public:
|
||||
|
||||
/**
|
||||
* Updates the last-use times on the given sessions to be greater than
|
||||
* or equal to the given time.
|
||||
*
|
||||
* Returns a list of sessions for which no authoritative record was found,
|
||||
* and hence were not refreshed. Returns an error if a networking issue occurred.
|
||||
* or equal to the given time. Returns an error if a networking issue occurred.
|
||||
*/
|
||||
virtual Status refreshSessions(OperationContext* opCtx,
|
||||
const LogicalSessionRecordSet& sessions,
|
||||
|
Loading…
Reference in New Issue
Block a user