summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSara Golemon <sara.golemon@mongodb.com>2021-11-09 20:20:06 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-12-20 18:46:05 +0000
commit68827214ba8746851a29454b4ef405ef629d7721 (patch)
tree69e11d36840a96cbfa122664626a39c48b4f8e81
parentb53fc88485f274d9b20cc93b53058f77d8859d93 (diff)
downloadmongo-68827214ba8746851a29454b4ef405ef629d7721.tar.gz
SERVER-61616 Add allowedWithTenant() command property
-rw-r--r--jstests/auth/security_token.js11
-rw-r--r--src/mongo/db/commands.h7
-rw-r--r--src/mongo/db/commands/connection_status.cpp4
-rw-r--r--src/mongo/db/commands/dbcommands.cpp26
-rw-r--r--src/mongo/db/commands/generic.cpp22
-rw-r--r--src/mongo/db/commands/generic_servers.cpp31
-rw-r--r--src/mongo/db/service_entry_point_common.cpp4
-rw-r--r--src/mongo/s/commands/cluster_build_info.cpp28
8 files changed, 93 insertions, 40 deletions
diff --git a/jstests/auth/security_token.js b/jstests/auth/security_token.js
index a1a0c6c2d3d..e0972118af7 100644
--- a/jstests/auth/security_token.js
+++ b/jstests/auth/security_token.js
@@ -112,16 +112,17 @@ function runTest(conn, enabled, rst = undefined) {
// Negative test, logMessage requires logMessage privilege on cluster (not granted)
assert.commandFailed(tokenDB.runCommand({logMessage: 'This is a test'}));
- // Positive test, writing to a new collection.
- assert.writeOK(tokenConn.getDB('test').coll1.insert({x: 1}));
+ // CRUD operations not yet supported in multitenancy using security token.
+ assert.writeError(tokenConn.getDB('test').coll1.insert({x: 1}));
const log = checkLog.getGlobalLog(conn).map((l) => JSON.parse(l));
- // We performed 4 commands as a token auth'd user.
- // We should see four post-operation logout events.
+ // We successfully dispatched 2 commands as a token auth'd user.
+ // The failed commands did not dispatch because they are forbidden in multitenancy.
+ // We should see two post-operation logout events.
const logoutMessages = log.filter((l) => (l.id === kLogoutMessageID));
assert.eq(logoutMessages.length,
- 4,
+ 2,
'Unexpected number of logout messages: ' + tojson(logoutMessages));
// None of those authorization sessions should remain active into their next requests.
diff --git a/src/mongo/db/commands.h b/src/mongo/db/commands.h
index 146ca75dca7..778c62cb2b6 100644
--- a/src/mongo/db/commands.h
+++ b/src/mongo/db/commands.h
@@ -579,6 +579,13 @@ public:
return true;
}
+ /**
+ * By default, no newly created command is permitted under multitenancy.
+ * Implementations must override this to true to permit use.
+ */
+ virtual bool allowedWithSecurityToken() const {
+ return false;
+ }
/**
* Get the authorization contract for this command. nullptr means no contract has been
diff --git a/src/mongo/db/commands/connection_status.cpp b/src/mongo/db/commands/connection_status.cpp
index da2090b9d1b..1e688d2274b 100644
--- a/src/mongo/db/commands/connection_status.cpp
+++ b/src/mongo/db/commands/connection_status.cpp
@@ -114,6 +114,10 @@ public:
return false;
}
+ bool allowedWithSecurityToken() const final {
+ return true;
+ }
+
AllowedOnSecondary secondaryAllowed(ServiceContext*) const final {
return AllowedOnSecondary::kAlways;
}
diff --git a/src/mongo/db/commands/dbcommands.cpp b/src/mongo/db/commands/dbcommands.cpp
index 78b5f25c93c..72720f43f44 100644
--- a/src/mongo/db/commands/dbcommands.cpp
+++ b/src/mongo/db/commands/dbcommands.cpp
@@ -715,24 +715,30 @@ class CmdBuildInfo : public BasicCommand {
public:
CmdBuildInfo() : BasicCommand("buildInfo", "buildinfo") {}
- AllowedOnSecondary secondaryAllowed(ServiceContext*) const override {
+ AllowedOnSecondary secondaryAllowed(ServiceContext*) const final {
return AllowedOnSecondary::kAlways;
}
- bool requiresAuth() const override {
+ bool requiresAuth() const final {
return false;
}
- virtual bool adminOnly() const {
+ bool adminOnly() const final {
return false;
}
- virtual bool supportsWriteConcern(const BSONObj& cmd) const override {
+
+ bool allowedWithSecurityToken() const final {
+ return true;
+ }
+
+ bool supportsWriteConcern(const BSONObj& cmd) const final {
return false;
}
- virtual void addRequiredPrivileges(const std::string& dbname,
- const BSONObj& cmdObj,
- std::vector<Privilege>* out) const {} // No auth required
- std::string help() const override {
+
+ void addRequiredPrivileges(const std::string& dbname,
+ const BSONObj& cmdObj,
+ std::vector<Privilege>* out) const final {} // No auth required
+ std::string help() const final {
return "get version #, etc.\n"
"{ buildinfo:1 }";
}
@@ -740,13 +746,13 @@ public:
bool run(OperationContext* opCtx,
const std::string& dbname,
const BSONObj& jsobj,
- BSONObjBuilder& result) {
+ BSONObjBuilder& result) final {
VersionInfoInterface::instance().appendBuildInfo(&result);
appendStorageEngineList(opCtx->getServiceContext(), &result);
return true;
}
- Future<void> runAsync(std::shared_ptr<RequestExecutionContext> rec, std::string) override {
+ Future<void> runAsync(std::shared_ptr<RequestExecutionContext> rec, std::string) final {
auto opCtx = rec->getOpCtx();
return BuildInfoExecutor::get(opCtx->getServiceContext())->schedule(std::move(rec));
}
diff --git a/src/mongo/db/commands/generic.cpp b/src/mongo/db/commands/generic.cpp
index 1a6e03f1f94..e472bed49d7 100644
--- a/src/mongo/db/commands/generic.cpp
+++ b/src/mongo/db/commands/generic.cpp
@@ -54,32 +54,36 @@ using std::vector;
class PingCommand : public PingCmdVersion1Gen<PingCommand> {
public:
- AllowedOnSecondary secondaryAllowed(ServiceContext*) const override {
+ AllowedOnSecondary secondaryAllowed(ServiceContext*) const final {
return AllowedOnSecondary::kAlways;
}
- std::string help() const override {
+
+ std::string help() const final {
return "a way to check that the server is alive. responds immediately even if server is "
"in a db lock.";
}
- virtual void addRequiredPrivileges(const std::string& dbname,
- const BSONObj& cmdObj,
- std::vector<Privilege>* out) const {} // No auth required
- virtual bool requiresAuth() const override {
+
+ bool requiresAuth() const final {
return false;
}
+
+ bool allowedWithSecurityToken() const final {
+ return true;
+ }
+
class Invocation final : public InvocationBaseGen {
public:
using InvocationBaseGen::InvocationBaseGen;
- virtual bool supportsWriteConcern() const override {
+ bool supportsWriteConcern() const final {
return false;
}
- virtual bool allowsAfterClusterTime() const override {
+ bool allowsAfterClusterTime() const final {
return false;
}
NamespaceString ns() const override {
return NamespaceString(request().getDbName());
}
- virtual Reply typedRun(OperationContext* opCtx) override {
+ Reply typedRun(OperationContext* opCtx) final {
// IMPORTANT: Don't put anything in here that might lock db - including authentication
return Reply{};
}
diff --git a/src/mongo/db/commands/generic_servers.cpp b/src/mongo/db/commands/generic_servers.cpp
index 72ed3f745dd..17df971cd85 100644
--- a/src/mongo/db/commands/generic_servers.cpp
+++ b/src/mongo/db/commands/generic_servers.cpp
@@ -54,12 +54,17 @@
namespace mongo {
namespace {
-template <typename RequestT, bool AdminOnly = true>
-class GenericTC : public TypedCommand<GenericTC<RequestT, AdminOnly>> {
+struct AdminOnlyNoTenant {
+ static constexpr bool kAdminOnly = true;
+ static constexpr bool kAllowedWithSecurityToken = false;
+};
+
+template <typename RequestT, typename Traits = AdminOnlyNoTenant>
+class GenericTC : public TypedCommand<GenericTC<RequestT, Traits>> {
public:
using Request = RequestT;
using Reply = typename RequestT::Reply;
- using TC = TypedCommand<GenericTC<RequestT, AdminOnly>>;
+ using TC = TypedCommand<GenericTC<RequestT, Traits>>;
class Invocation final : public TC::InvocationBase {
public:
@@ -79,7 +84,11 @@ public:
};
bool adminOnly() const final {
- return AdminOnly;
+ return Traits::kAdminOnly;
+ }
+
+ bool allowedWithSecurityToken() const final {
+ return Traits::kAllowedWithSecurityToken;
}
typename TC::AllowedOnSecondary secondaryAllowed(ServiceContext*) const final {
@@ -87,8 +96,13 @@ public:
}
};
+struct AnyDbAllowTenant {
+ static constexpr bool kAdminOnly = false;
+ static constexpr bool kAllowedWithSecurityToken = true;
+};
+
// { features: 1 }
-using FeaturesCmd = GenericTC<FeaturesCommand, false>;
+using FeaturesCmd = GenericTC<FeaturesCommand, AnyDbAllowTenant>;
template <>
void FeaturesCmd::Invocation::doCheckAuthorization(OperationContext* opCtx) const {
if (request().getOidReset().value_or(false)) {
@@ -116,8 +130,13 @@ FeaturesReply FeaturesCmd::Invocation::typedRun(OperationContext*) {
}
FeaturesCmd featuresCmd;
+struct AnyDbNoTenant {
+ static constexpr bool kAdminOnly = false;
+ static constexpr bool kAllowedWithSecurityToken = false;
+};
+
// { hostInfo: 1 }
-using HostInfoCmd = GenericTC<HostInfoCommand, false>;
+using HostInfoCmd = GenericTC<HostInfoCommand, AnyDbNoTenant>;
template <>
void HostInfoCmd::Invocation::doCheckAuthorization(OperationContext* opCtx) const {
auto* as = AuthorizationSession::get(opCtx->getClient());
diff --git a/src/mongo/db/service_entry_point_common.cpp b/src/mongo/db/service_entry_point_common.cpp
index 8fa9892aacf..1dd97bef48b 100644
--- a/src/mongo/db/service_entry_point_common.cpp
+++ b/src/mongo/db/service_entry_point_common.cpp
@@ -1290,6 +1290,10 @@ void ExecCommandDatabase::_initiateCommand() {
});
rpc::readRequestMetadata(opCtx, request, command->requiresAuth());
+ uassert(ErrorCodes::Unauthorized,
+ str::stream() << "Command " << command->getName()
+ << " is not supported in multitenancy mode",
+ command->allowedWithSecurityToken() || auth::getSecurityToken(opCtx) == boost::none);
_tokenAuthorizationSessionGuard.emplace(opCtx);
rpc::TrackingMetadata::get(opCtx).initWithOperName(command->getName());
diff --git a/src/mongo/s/commands/cluster_build_info.cpp b/src/mongo/s/commands/cluster_build_info.cpp
index 0cf74905f7b..309c1b1c3cd 100644
--- a/src/mongo/s/commands/cluster_build_info.cpp
+++ b/src/mongo/s/commands/cluster_build_info.cpp
@@ -68,23 +68,31 @@ class ClusterCmdBuildInfo : public BasicCommand {
public:
ClusterCmdBuildInfo() : BasicCommand("buildInfo", "buildinfo") {}
- AllowedOnSecondary secondaryAllowed(ServiceContext*) const override {
+ AllowedOnSecondary secondaryAllowed(ServiceContext*) const final {
return AllowedOnSecondary::kAlways;
}
- bool requiresAuth() const override {
+ bool requiresAuth() const final {
return false;
}
- virtual bool adminOnly() const {
+
+ bool adminOnly() const final {
return false;
}
- virtual bool supportsWriteConcern(const BSONObj& cmd) const override {
+
+ bool allowedWithSecurityToken() const {
+ return true;
+ }
+
+ bool supportsWriteConcern(const BSONObj& cmd) const final {
return false;
}
- virtual void addRequiredPrivileges(const std::string& dbname,
- const BSONObj& cmdObj,
- std::vector<Privilege>* out) const {} // No auth required
- std::string help() const override {
+
+ void addRequiredPrivileges(const std::string& dbname,
+ const BSONObj& cmdObj,
+ std::vector<Privilege>* out) const final {} // No auth required
+
+ std::string help() const final {
return "get version #, etc.\n"
"{ buildinfo:1 }";
}
@@ -92,12 +100,12 @@ public:
bool run(OperationContext* opCtx,
const std::string& dbname,
const BSONObj& jsobj,
- BSONObjBuilder& result) {
+ BSONObjBuilder& result) final {
VersionInfoInterface::instance().appendBuildInfo(&result);
return true;
}
- Future<void> runAsync(std::shared_ptr<RequestExecutionContext> rec, std::string) override {
+ Future<void> runAsync(std::shared_ptr<RequestExecutionContext> rec, std::string) final {
auto opCtx = rec->getOpCtx();
return ClusterBuildInfoExecutor::get(opCtx->getServiceContext())->schedule(std::move(rec));
}