summaryrefslogtreecommitdiff
path: root/src/mongo/db/auth/authorization_session_impl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/auth/authorization_session_impl.cpp')
-rw-r--r--src/mongo/db/auth/authorization_session_impl.cpp329
1 files changed, 5 insertions, 324 deletions
diff --git a/src/mongo/db/auth/authorization_session_impl.cpp b/src/mongo/db/auth/authorization_session_impl.cpp
index 0f131648506..20ed5d956a6 100644
--- a/src/mongo/db/auth/authorization_session_impl.cpp
+++ b/src/mongo/db/auth/authorization_session_impl.cpp
@@ -33,6 +33,7 @@
#include "mongo/db/auth/authorization_session_impl.h"
+#include <array>
#include <string>
#include <vector>
@@ -43,17 +44,10 @@
#include "mongo/db/auth/action_type.h"
#include "mongo/db/auth/authz_session_external_state.h"
#include "mongo/db/auth/privilege.h"
-#include "mongo/db/auth/restriction_environment.h"
-#include "mongo/db/auth/security_key.h"
-#include "mongo/db/auth/user_management_commands_parser.h"
#include "mongo/db/bson/dotted_path_support.h"
-#include "mongo/db/catalog/document_validation.h"
#include "mongo/db/client.h"
-#include "mongo/db/jsobj.h"
#include "mongo/db/namespace_string.h"
#include "mongo/db/operation_context.h"
-#include "mongo/db/pipeline/aggregation_request_helper.h"
-#include "mongo/db/pipeline/lite_parsed_pipeline.h"
#include "mongo/logv2/log.h"
#include "mongo/util/assert_util.h"
#include "mongo/util/str.h"
@@ -62,7 +56,6 @@ namespace mongo {
namespace dps = ::mongo::dotted_path_support;
using std::vector;
-
namespace {
std::unique_ptr<AuthorizationSession> authorizationSessionCreateImpl(
@@ -77,34 +70,6 @@ auto authorizationSessionCreateRegistration =
constexpr StringData ADMIN_DBNAME = "admin"_sd;
-// Checks if this connection has the privileges necessary to create or modify the view 'viewNs'
-// to be a view on 'viewOnNs' with pipeline 'viewPipeline'. Call this function after verifying
-// that the user has the 'createCollection' or 'collMod' action, respectively.
-Status checkAuthForCreateOrModifyView(AuthorizationSession* authzSession,
- const NamespaceString& viewNs,
- const NamespaceString& viewOnNs,
- const BSONArray& viewPipeline,
- bool isMongos) {
- // It's safe to allow a user to create or modify a view if they can't read it anyway.
- if (!authzSession->isAuthorizedForActionsOnNamespace(viewNs, ActionType::find)) {
- return Status::OK();
- }
-
- auto request = aggregation_request_helper::parseFromBSON(
- viewNs,
- BSON("aggregate" << viewOnNs.coll() << "pipeline" << viewPipeline << "cursor" << BSONObj()
- << "$db" << viewOnNs.db()),
- boost::none,
- false);
-
- auto statusWithPrivs = authzSession->getPrivilegesForAggregate(viewOnNs, request, isMongos);
- PrivilegeVector privileges = uassertStatusOK(statusWithPrivs);
- if (!authzSession->isAuthorizedForPrivileges(privileges)) {
- return Status(ErrorCodes::Unauthorized, "unauthorized");
- }
- return Status::OK();
-}
-
} // namespace
AuthorizationSessionImpl::AuthorizationSessionImpl(
@@ -267,274 +232,6 @@ PrivilegeVector AuthorizationSessionImpl::getDefaultPrivileges() {
return defaultPrivileges;
}
-StatusWith<PrivilegeVector> AuthorizationSessionImpl::getPrivilegesForAggregate(
- const NamespaceString& nss, const AggregateCommand& request, bool isMongos) {
- if (!nss.isValid()) {
- return Status(ErrorCodes::InvalidNamespace,
- str::stream() << "Invalid input namespace, " << nss.ns());
- }
-
- PrivilegeVector privileges;
-
- // If this connection does not need to be authenticated (for instance, if auth is disabled),
- // returns an empty requirements set.
- if (_externalState->shouldIgnoreAuthChecks()) {
- return privileges;
- }
-
- const auto& pipeline = request.getPipeline();
-
- // If the aggregation pipeline is empty, confirm the user is authorized for find on 'nss'.
- if (pipeline.empty()) {
- Privilege currentPriv =
- Privilege(ResourcePattern::forExactNamespace(nss), ActionType::find);
- Privilege::addPrivilegeToPrivilegeVector(&privileges, currentPriv);
- return privileges;
- }
-
- // If the first stage of the pipeline is not an initial source, the pipeline is implicitly
- // reading documents from the underlying collection. The client must be authorized to do so.
- auto liteParsedDocSource = LiteParsedDocumentSource::parse(nss, pipeline[0]);
- if (!liteParsedDocSource->isInitialSource()) {
- Privilege currentPriv =
- Privilege(ResourcePattern::forExactNamespace(nss), ActionType::find);
- Privilege::addPrivilegeToPrivilegeVector(&privileges, currentPriv);
- }
-
- // Confirm privileges for the pipeline.
- for (auto&& pipelineStage : pipeline) {
- liteParsedDocSource = LiteParsedDocumentSource::parse(nss, pipelineStage);
- PrivilegeVector currentPrivs = liteParsedDocSource->requiredPrivileges(
- isMongos, request.getBypassDocumentValidation().value_or(false));
- Privilege::addPrivilegesToPrivilegeVector(&privileges, currentPrivs);
- }
- return privileges;
-}
-
-Status AuthorizationSessionImpl::checkAuthForFind(const NamespaceString& ns, bool hasTerm) {
- if (MONGO_unlikely(ns.isCommand())) {
- return Status(ErrorCodes::InternalError,
- str::stream() << "Checking query auth on command namespace " << ns.ns());
- }
- if (!isAuthorizedForActionsOnNamespace(ns, ActionType::find)) {
- return Status(ErrorCodes::Unauthorized,
- str::stream() << "not authorized for query on " << ns.ns());
- }
-
- // Only internal clients (such as other nodes in a replica set) are allowed to use
- // the 'term' field in a find operation. Use of this field could trigger changes
- // in the receiving server's replication state and should be protected.
- if (hasTerm &&
- !isAuthorizedForActionsOnResource(ResourcePattern::forClusterResource(),
- ActionType::internal)) {
- return Status(ErrorCodes::Unauthorized,
- str::stream() << "not authorized for query with term on " << ns.ns());
- }
-
- return Status::OK();
-}
-
-Status AuthorizationSessionImpl::checkAuthForGetMore(const NamespaceString& ns,
- long long cursorID,
- bool hasTerm) {
- // Since users can only getMore their own cursors, we verify that a user either is authenticated
- // or does not need to be.
- if (!_externalState->shouldIgnoreAuthChecks() && !isAuthenticated()) {
- return Status(ErrorCodes::Unauthorized,
- str::stream() << "not authorized for getMore on " << ns.db());
- }
-
- // Only internal clients (such as other nodes in a replica set) are allowed to use
- // the 'term' field in a getMore operation. Use of this field could trigger changes
- // in the receiving server's replication state and should be protected.
- if (hasTerm &&
- !isAuthorizedForActionsOnResource(ResourcePattern::forClusterResource(),
- ActionType::internal)) {
- return Status(ErrorCodes::Unauthorized,
- str::stream() << "not authorized for getMore with term on " << ns.ns());
- }
-
- return Status::OK();
-}
-
-Status AuthorizationSessionImpl::checkAuthForInsert(OperationContext* opCtx,
- const NamespaceString& ns) {
- ActionSet required{ActionType::insert};
- if (DocumentValidationSettings::get(opCtx).isSchemaValidationDisabled()) {
- required.addAction(ActionType::bypassDocumentValidation);
- }
- if (!isAuthorizedForActionsOnNamespace(ns, required)) {
- return Status(ErrorCodes::Unauthorized,
- str::stream() << "not authorized for insert on " << ns.ns());
- }
-
- return Status::OK();
-}
-
-Status AuthorizationSessionImpl::checkAuthForUpdate(OperationContext* opCtx,
- const NamespaceString& ns,
- const BSONObj& query,
- const write_ops::UpdateModification& update,
- bool upsert) {
- ActionSet required{ActionType::update};
- StringData operationType = "update"_sd;
-
- if (upsert) {
- required.addAction(ActionType::insert);
- operationType = "upsert"_sd;
- }
-
- if (DocumentValidationSettings::get(opCtx).isSchemaValidationDisabled()) {
- required.addAction(ActionType::bypassDocumentValidation);
- }
-
- if (!isAuthorizedForActionsOnNamespace(ns, required)) {
- return Status(ErrorCodes::Unauthorized,
- str::stream() << "not authorized for " << operationType << " on " << ns.ns());
- }
-
- return Status::OK();
-}
-
-Status AuthorizationSessionImpl::checkAuthForDelete(OperationContext* opCtx,
- const NamespaceString& ns,
- const BSONObj& query) {
- if (!isAuthorizedForActionsOnNamespace(ns, ActionType::remove)) {
- return Status(ErrorCodes::Unauthorized,
- str::stream() << "not authorized to remove from " << ns.ns());
- }
- return Status::OK();
-}
-
-Status AuthorizationSessionImpl::checkAuthForKillCursors(const NamespaceString& ns,
- UserNameIterator cursorOwner) {
- if (isAuthorizedForActionsOnResource(ResourcePattern::forClusterResource(),
- ActionType::killAnyCursor)) {
- return Status::OK();
- }
-
- if (isCoauthorizedWith(cursorOwner)) {
- return Status::OK();
- }
-
- ResourcePattern target;
- if (ns.isListCollectionsCursorNS()) {
- target = ResourcePattern::forDatabaseName(ns.db());
- } else {
- target = ResourcePattern::forExactNamespace(ns);
- }
-
- if (isAuthorizedForActionsOnResource(target, ActionType::killAnyCursor)) {
- return Status::OK();
- }
-
- return Status(ErrorCodes::Unauthorized,
- str::stream() << "not authorized to kill cursor on " << ns.ns());
-}
-
-Status AuthorizationSessionImpl::checkAuthForCreate(const CreateCommand& cmd, bool isMongos) {
- auto ns = cmd.getNamespace();
- if (cmd.getCapped() && !isAuthorizedForActionsOnNamespace(ns, ActionType::convertToCapped)) {
- return {ErrorCodes::Unauthorized, "unauthorized"};
- }
-
- const bool hasCreateCollectionAction =
- isAuthorizedForActionsOnNamespace(ns, ActionType::createCollection);
-
- // If attempting to create a view, check for additional required privileges.
- if (auto optViewOn = cmd.getViewOn()) {
- // You need the createCollection action on this namespace; the insert action is not
- // sufficient.
- if (!hasCreateCollectionAction) {
- return {ErrorCodes::Unauthorized, "unauthorized"};
- }
-
- // Parse the viewOn namespace and the pipeline. If no pipeline was specified, use the empty
- // pipeline.
- NamespaceString viewOnNs(ns.db(), optViewOn.get());
- auto pipeline = cmd.getPipeline().get_value_or(std::vector<BSONObj>());
- BSONArrayBuilder pipelineArray;
- for (const auto& stage : pipeline) {
- pipelineArray.append(stage);
- }
- return checkAuthForCreateOrModifyView(this, ns, viewOnNs, pipelineArray.arr(), isMongos);
- }
-
- // To create a regular collection, ActionType::createCollection or ActionType::insert are
- // both acceptable.
- if (hasCreateCollectionAction || isAuthorizedForActionsOnNamespace(ns, ActionType::insert)) {
- return Status::OK();
- }
-
- return {ErrorCodes::Unauthorized, "unauthorized"};
-}
-
-Status AuthorizationSessionImpl::checkAuthForCollMod(const NamespaceString& ns,
- const BSONObj& cmdObj,
- bool isMongos) {
- if (!isAuthorizedForActionsOnNamespace(ns, ActionType::collMod)) {
- return Status(ErrorCodes::Unauthorized, "unauthorized");
- }
-
- // Check for additional required privileges if attempting to modify a view. When auth is
- // enabled, users must specify both "viewOn" and "pipeline" together. This prevents a user from
- // exposing more information in the original underlying namespace by only changing "pipeline",
- // or looking up more information via the original pipeline by only changing "viewOn".
- const bool hasViewOn = cmdObj.hasField("viewOn");
- const bool hasPipeline = cmdObj.hasField("pipeline");
- if (hasViewOn != hasPipeline) {
- return Status(
- ErrorCodes::InvalidOptions,
- "Must specify both 'viewOn' and 'pipeline' when modifying a view and auth is enabled");
- }
- if (hasViewOn) {
- NamespaceString viewOnNs(ns.db(), cmdObj["viewOn"].checkAndGetStringData());
- auto viewPipeline = BSONArray(cmdObj["pipeline"].Obj());
- return checkAuthForCreateOrModifyView(this, ns, viewOnNs, viewPipeline, isMongos);
- }
-
- return Status::OK();
-}
-
-Status AuthorizationSessionImpl::checkAuthorizedToGrantPrivilege(const Privilege& privilege) {
- const ResourcePattern& resource = privilege.getResourcePattern();
- if (resource.isDatabasePattern() || resource.isExactNamespacePattern()) {
- if (!isAuthorizedForActionsOnResource(
- ResourcePattern::forDatabaseName(resource.databaseToMatch()),
- ActionType::grantRole)) {
- return Status(ErrorCodes::Unauthorized,
- str::stream() << "Not authorized to grant privileges on the "
- << resource.databaseToMatch() << "database");
- }
- } else if (!isAuthorizedForActionsOnResource(ResourcePattern::forDatabaseName("admin"),
- ActionType::grantRole)) {
- return Status(ErrorCodes::Unauthorized,
- "To grant privileges affecting multiple databases or the cluster,"
- " must be authorized to grant roles from the admin database");
- }
- return Status::OK();
-}
-
-
-Status AuthorizationSessionImpl::checkAuthorizedToRevokePrivilege(const Privilege& privilege) {
- const ResourcePattern& resource = privilege.getResourcePattern();
- if (resource.isDatabasePattern() || resource.isExactNamespacePattern()) {
- if (!isAuthorizedForActionsOnResource(
- ResourcePattern::forDatabaseName(resource.databaseToMatch()),
- ActionType::revokeRole)) {
- return Status(ErrorCodes::Unauthorized,
- str::stream() << "Not authorized to revoke privileges on the "
- << resource.databaseToMatch() << "database");
- }
- } else if (!isAuthorizedForActionsOnResource(ResourcePattern::forDatabaseName("admin"),
- ActionType::revokeRole)) {
- return Status(ErrorCodes::Unauthorized,
- "To revoke privileges affecting multiple databases or the cluster,"
- " must be authorized to revoke roles from the admin database");
- }
- return Status::OK();
-}
-
bool AuthorizationSessionImpl::isAuthorizedToParseNamespaceElement(const BSONElement& element) {
const bool isUUID = element.type() == BinData && element.binDataType() == BinDataType::newUUID;
@@ -586,16 +283,6 @@ bool AuthorizationSessionImpl::isAuthorizedToCreateRole(const RoleName& roleName
return false;
}
-bool AuthorizationSessionImpl::isAuthorizedToGrantRole(const RoleName& role) {
- return isAuthorizedForActionsOnResource(ResourcePattern::forDatabaseName(role.getDB()),
- ActionType::grantRole);
-}
-
-bool AuthorizationSessionImpl::isAuthorizedToRevokeRole(const RoleName& role) {
- return isAuthorizedForActionsOnResource(ResourcePattern::forDatabaseName(role.getDB()),
- ActionType::revokeRole);
-}
-
bool AuthorizationSessionImpl::isAuthorizedForPrivilege(const Privilege& privilege) {
if (_externalState->shouldIgnoreAuthChecks())
return true;
@@ -717,16 +404,6 @@ bool AuthorizationSessionImpl::isAuthorizedToChangeAsUser(const UserName& userNa
return actions.contains(actionType);
}
-bool AuthorizationSessionImpl::isAuthorizedToChangeOwnPasswordAsUser(const UserName& userName) {
- return AuthorizationSessionImpl::isAuthorizedToChangeAsUser(userName,
- ActionType::changeOwnPassword);
-}
-
-bool AuthorizationSessionImpl::isAuthorizedToChangeOwnCustomDataAsUser(const UserName& userName) {
- return AuthorizationSessionImpl::isAuthorizedToChangeAsUser(userName,
- ActionType::changeOwnCustomData);
-}
-
StatusWith<PrivilegeVector> AuthorizationSessionImpl::checkAuthorizedToListCollections(
StringData dbname, const BSONObj& cmdObj) {
if (cmdObj["authorizedCollections"].trueValue() && cmdObj["nameOnly"].trueValue() &&
@@ -753,6 +430,10 @@ bool AuthorizationSessionImpl::isAuthenticatedAsUserWithRole(const RoleName& rol
return false;
}
+bool AuthorizationSessionImpl::shouldIgnoreAuthChecks() {
+ return _externalState->shouldIgnoreAuthChecks();
+}
+
bool AuthorizationSessionImpl::isAuthenticated() {
return _authenticatedUsers.begin() != _authenticatedUsers.end();
}