diff options
Diffstat (limited to 'src')
68 files changed, 2922 insertions, 2299 deletions
diff --git a/src/mongo/SConscript b/src/mongo/SConscript index ab5d89cb24c..3cb34a42bcf 100644 --- a/src/mongo/SConscript +++ b/src/mongo/SConscript @@ -292,6 +292,8 @@ env.Library( "db/db.cpp", ], LIBDEPS_PRIVATE=[ + 'base', + 'db/auth/authmongod', 'db/catalog/health_log', 'db/commands/mongod', 'db/commands/mongod_fcv', @@ -462,8 +464,9 @@ if not has_option('noshell') and usemozjs: "shell/shell_utils_launcher.cpp", ], LIBDEPS=[ - 'db/logical_session_id_helpers', 'db/catalog/index_key_validate', + 'db/logical_session_id_helpers', + 'db/mongohasher', 'db/query/command_request_response', 'db/query/query_request', 'db/server_options_core', diff --git a/src/mongo/base/counter.h b/src/mongo/base/counter.h index fad095fc385..02e2318a03a 100644 --- a/src/mongo/base/counter.h +++ b/src/mongo/base/counter.h @@ -1,5 +1,3 @@ -// counter.h - /** * Copyright (C) 2008-2012 10gen Inc. * @@ -66,4 +64,4 @@ public: private: AtomicInt64 _counter; }; -} +} // namespace mongo diff --git a/src/mongo/base/shim.h b/src/mongo/base/shim.h index 49a21dd81a4..025c6a2a65c 100644 --- a/src/mongo/base/shim.h +++ b/src/mongo/base/shim.h @@ -146,7 +146,7 @@ const bool checkShimsViaTUHook = false; struct LibTUHookTypeBase { \ LibTUHookTypeBase(); \ }; \ - template <bool required = mongo::checkShimsViaTUHook> \ + template <bool required = true> \ struct LibTUHookType : LibTUHookTypeBase {}; \ using LibTUHook = LibTUHookType<>; \ struct ImplTUHookTypeBase { \ @@ -166,7 +166,7 @@ const bool checkShimsViaTUHook = false; return this; \ } \ MongoShimImplGuts* lib(const LibTUHook* const) { \ - MONGO_SHIM_TU_HOOK(LibTUHook); \ + LibTUHook{}; \ return this; \ } \ MongoShimImplGuts* impl(const ImplTUHook* const) { \ diff --git a/src/mongo/client/SConscript b/src/mongo/client/SConscript index 5f054e5588d..91d5091aa77 100644 --- a/src/mongo/client/SConscript +++ b/src/mongo/client/SConscript @@ -176,6 +176,8 @@ clientDriverEnv.Library( LIBDEPS=[ '$BUILD_DIR/mongo/db/auth/authcommon', '$BUILD_DIR/mongo/db/dbmessage', + '$BUILD_DIR/mongo/db/query/command_request_response', + '$BUILD_DIR/mongo/db/query/query_request', '$BUILD_DIR/mongo/db/wire_version', '$BUILD_DIR/mongo/db/write_concern_options', '$BUILD_DIR/mongo/executor/connection_pool_stats', @@ -314,7 +316,7 @@ env.CppUnitTest( source='fetcher_test.cpp', LIBDEPS=[ 'fetcher', - '$BUILD_DIR/mongo/db/auth/authorization_manager_mock_init', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/service_context_noop_init', '$BUILD_DIR/mongo/executor/thread_pool_task_executor_test_fixture', ], diff --git a/src/mongo/client/embedded/SConscript b/src/mongo/client/embedded/SConscript index deb1c7e5181..eecf1e3387d 100644 --- a/src/mongo/client/embedded/SConscript +++ b/src/mongo/client/embedded/SConscript @@ -15,7 +15,7 @@ env.Library( '$BUILD_DIR/mongo/transport/service_entry_point', ], LIBDEPS_PRIVATE=[ - '$BUILD_DIR/mongo/db/auth/authcore', + '$BUILD_DIR/mongo/db/auth/auth', '$BUILD_DIR/mongo/db/auth/authmongod', '$BUILD_DIR/mongo/db/command_can_run_here', '$BUILD_DIR/mongo/db/rw_concern_d', @@ -104,7 +104,6 @@ env.Library( '$BUILD_DIR/mongo/base', ], LIBDEPS_PRIVATE=[ - '$BUILD_DIR/mongo/db/authz_manager_external_state_factory_d', '$BUILD_DIR/mongo/db/catalog/catalog_impl', '$BUILD_DIR/mongo/db/commands/fsync_locked', '$BUILD_DIR/mongo/db/commands/mongod_fcv', diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript index 921c8f01c9e..046fac61168 100644 --- a/src/mongo/db/SConscript +++ b/src/mongo/db/SConscript @@ -133,6 +133,7 @@ env.CppUnitTest( 'common', 'op_observer_d', 'service_context_d_test_fixture', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/repl/replmocks', ], ) @@ -231,7 +232,7 @@ env.CppUnitTest( 'logical_session_id', 'logical_session_id_helpers', 'service_context', - '$BUILD_DIR/mongo/db/auth/authorization_manager_mock_init', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/service_context_noop_init', '$BUILD_DIR/mongo/unittest/concurrency', '$BUILD_DIR/mongo/util/clock_source_mock', @@ -536,10 +537,13 @@ env.Library( '$BUILD_DIR/mongo/base', ], LIBDEPS_PRIVATE=[ + '$BUILD_DIR/mongo/bson/mutable/mutable_bson', '$BUILD_DIR/mongo/rpc/rpc', + '$BUILD_DIR/mongo/rpc/command_status', 'audit', 'command_generic_argument', 'commands/server_status_core', + 'namespace_string', ], ) @@ -577,7 +581,7 @@ env.CppUnitTest( '$BUILD_DIR/mongo/idl/idl_parser', "commands", "service_context_noop_init", - "auth/authorization_manager_mock_init", + "auth/authmocks", "repl/replmocks", ], ) @@ -638,7 +642,7 @@ env.CppUnitTest( 'catalog_raii_test.cpp', ], LIBDEPS=[ - 'auth/authorization_manager_mock_init', + 'auth/authmocks', 'catalog/database_holder_mock', 'catalog_raii', 'service_context_noop_init', @@ -695,7 +699,7 @@ env.Library( ], LIBDEPS=[ '$BUILD_DIR/mongo/base', - '$BUILD_DIR/mongo/db/auth/authmongod', + '$BUILD_DIR/mongo/db/auth/auth', '$BUILD_DIR/mongo/db/concurrency/lock_manager', '$BUILD_DIR/mongo/db/ops/write_ops_parsers', '$BUILD_DIR/mongo/db/s/sharding', @@ -754,8 +758,8 @@ env.Library( '$BUILD_DIR/mongo/base', ], LIBDEPS_PRIVATE=[ - '$BUILD_DIR/mongo/db/auth/authcore', - '$BUILD_DIR/mongo/db/auth/authmongod', + '$BUILD_DIR/mongo/db/auth/auth', + '$BUILD_DIR/mongo/db/auth/authprivilege', '$BUILD_DIR/mongo/db/command_can_run_here', '$BUILD_DIR/mongo/db/ops/write_ops_exec', '$BUILD_DIR/mongo/db/rw_concern_d', @@ -897,16 +901,6 @@ env.Library( ) env.Library( - target="authz_manager_external_state_factory_d", - source=[ - "authz_manager_external_state_factory_d.cpp", - ], - LIBDEPS=[ - 'auth/authmongod', - ], -) - -env.Library( target='query_exec', source=[ 'clientcursor.cpp', @@ -1004,6 +998,7 @@ env.Library( 'stats/serveronly_stats', 'storage/oplog_hack', 'storage/storage_options', + 'update/update_driver', ], ) @@ -1028,8 +1023,6 @@ env.Library( "$BUILD_DIR/mongo/util/elapsed_tracker", "$BUILD_DIR/mongo/util/net/network", "$BUILD_DIR/third_party/shim_snappy", - "auth/authmongod", - "authz_manager_external_state_factory_d", "background", "catalog/catalog_impl", "catalog/collection_options", @@ -1110,6 +1103,7 @@ env.Library( 'dbhelpers', 'repair_database', 'repl/drop_pending_collection_reaper', + 'repl/repl_settings', ], ) @@ -1136,7 +1130,8 @@ env.Library( ], LIBDEPS=[ 'logical_session_id', - '$BUILD_DIR/mongo/db/auth/authcore', + '$BUILD_DIR/mongo/db/auth/auth', + '$BUILD_DIR/mongo/db/auth/authprivilege', ], ) @@ -1162,7 +1157,7 @@ envWithAsio.CppUnitTest( ], LIBDEPS=[ '$BUILD_DIR/mongo/base', - '$BUILD_DIR/mongo/db/auth/authcore', + '$BUILD_DIR/mongo/db/auth/auth', '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/auth/authorization_session_for_test', '$BUILD_DIR/mongo/db/service_context_noop_init', @@ -1435,7 +1430,8 @@ env.Library( '$BUILD_DIR/mongo/base', ], LIBDEPS_PRIVATE=[ - '$BUILD_DIR/mongo/db/auth/authcore', + '$BUILD_DIR/mongo/db/auth/auth', + '$BUILD_DIR/mongo/db/auth/authprivilege', '$BUILD_DIR/mongo/idl/idl_parser', ], ) @@ -1560,7 +1556,8 @@ env.Library( 'service_context', 'signed_logical_time', 'time_proof_service', - '$BUILD_DIR/mongo/db/auth/authcore', + '$BUILD_DIR/mongo/db/auth/auth', + '$BUILD_DIR/mongo/db/auth/authprivilege', ], ) @@ -1612,6 +1609,7 @@ env.CppUnitTest( 'logical_time_validator_test.cpp', ], LIBDEPS=[ + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/s/catalog/dist_lock_manager_mock', '$BUILD_DIR/mongo/s/config_server_test_fixture', 'keys_collection_manager', @@ -1639,6 +1637,7 @@ env.Library( LIBDEPS= [ 'logical_clock', 'signed_logical_time', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/s/sharding_mongod_test_fixture', '$BUILD_DIR/mongo/util/clock_source_mock' ], @@ -1662,6 +1661,7 @@ env.CppUnitTest( 'keys_collection_manager_sharding_test.cpp', ], LIBDEPS=[ + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/s/catalog/dist_lock_manager_mock', '$BUILD_DIR/mongo/s/config_server_test_fixture', 'keys_collection_manager', @@ -1784,6 +1784,7 @@ env.CppUnitTest( ], LIBDEPS=[ 'query_exec', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/repl/mock_repl_coord_server_fixture', '$BUILD_DIR/mongo/client/read_preference' ], @@ -1795,6 +1796,7 @@ env.CppUnitTest( 'transaction_history_iterator_test.cpp', ], LIBDEPS=[ + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/repl/mock_repl_coord_server_fixture', 'query_exec', 'service_context_d', diff --git a/src/mongo/db/auth/SConscript b/src/mongo/db/auth/SConscript index ee7829fb043..38348507286 100644 --- a/src/mongo/db/auth/SConscript +++ b/src/mongo/db/auth/SConscript @@ -17,16 +17,45 @@ env.Library('auth_rolename', ['role_name.cpp'], ) env.Library( - target = 'authentication_restriction', - source = [ + target='authentication_restriction', + source=[ 'restriction_environment.cpp', ], - LIBDEPS = [ + LIBDEPS=[ '$BUILD_DIR/mongo/base', '$BUILD_DIR/mongo/util/net/network', ], ) +env.CppUnitTest( + target='restriction_test', + source='restriction_test.cpp', + LIBDEPS=[ + '$BUILD_DIR/mongo/base', + 'authentication_restriction', + ], +) + +# The Auth library should consist only of the shimmed API for Auth usage and the implementations of +# the data structures used in that API. No actual Auth subsystem implementation should exist in +# this library. +env.Library( + target='auth', + source=[ + 'authorization_manager.cpp', + 'authorization_session.cpp', + 'auth_decorations.cpp', + ], + LIBDEPS=[ + 'auth_rolename', + 'user_name', + '$BUILD_DIR/mongo/base', + '$BUILD_DIR/mongo/db/service_context', + '$BUILD_DIR/mongo/rpc/audit_metadata', + ], +) + + env.Library( target='user_name', source=[ @@ -37,55 +66,61 @@ env.Library( ], ) -env.CppUnitTest( - target = 'restriction_test', - source = 'restriction_test.cpp', - LIBDEPS = [ +env.Library( + target='user', + source=[ + 'user.cpp', + ], + LIBDEPS=[ + 'authprivilege', '$BUILD_DIR/mongo/base', - 'authentication_restriction', + '$BUILD_DIR/mongo/crypto/sha_block_${MONGO_CRYPTO}', ], ) +env.Library( + target='auth_impl_internal_local', + source=[ + 'authz_manager_external_state_local.cpp', + ], + LIBDEPS=[ + 'auth_impl_internal', + ], +) -# Just the data structures used env.Library( - target='authcore', + target='auth_impl_internal', source=[ - 'authorization_manager.cpp', - 'authorization_session.cpp', - 'auth_decorations.cpp', + 'authorization_manager_impl.cpp', + 'authorization_session_impl.cpp', 'authz_manager_external_state.cpp', - 'authz_manager_external_state_local.cpp', 'authz_session_external_state.cpp', 'role_graph.cpp', 'role_graph_update.cpp', 'role_graph_builtin_roles.cpp', - 'user.cpp', 'user_document_parser.cpp', - 'user_management_commands_parser.cpp', - 'user_set.cpp' + 'user_set.cpp', ], LIBDEPS=[ - 'address_restriction', - 'authprivilege', - 'auth_rolename', - 'authentication_restriction', - 'sasl_options', - 'user_name', - '$BUILD_DIR/mongo/base', - '$BUILD_DIR/mongo/base/secure_allocator', - '$BUILD_DIR/mongo/bson/mutable/mutable_bson', - '$BUILD_DIR/mongo/bson/util/bson_extract', - '$BUILD_DIR/mongo/db/catalog/document_validation', - '$BUILD_DIR/mongo/db/common', - '$BUILD_DIR/mongo/db/global_settings', - '$BUILD_DIR/mongo/db/namespace_string', - '$BUILD_DIR/mongo/db/pipeline/lite_parsed_document_source', - '$BUILD_DIR/mongo/db/service_context', - '$BUILD_DIR/mongo/db/update/update_driver', - '$BUILD_DIR/mongo/util/md5', - '$BUILD_DIR/mongo/util/icu', - '$BUILD_DIR/mongo/util/net/ssl_manager', + 'address_restriction', + 'auth', + 'auth_rolename', + 'authentication_restriction', + 'authprivilege', + 'sasl_options', + 'user', + '$BUILD_DIR/mongo/base', + '$BUILD_DIR/mongo/base/secure_allocator', + '$BUILD_DIR/mongo/bson/mutable/mutable_bson', + '$BUILD_DIR/mongo/bson/util/bson_extract', + '$BUILD_DIR/mongo/db/catalog/document_validation', + '$BUILD_DIR/mongo/db/common', + '$BUILD_DIR/mongo/db/global_settings', + '$BUILD_DIR/mongo/db/namespace_string', + '$BUILD_DIR/mongo/db/pipeline/lite_parsed_document_source', + '$BUILD_DIR/mongo/db/update/update_driver', + '$BUILD_DIR/mongo/util/icu', + '$BUILD_DIR/mongo/util/net/ssl_manager', ], ) @@ -94,11 +129,15 @@ env.Library( source=[ 'action_set.cpp', 'action_type.cpp', + 'impersonation_session.cpp', 'privilege.cpp', 'privilege_parser.cpp', 'resource_pattern.cpp', + 'user_management_commands_parser.cpp', ], LIBDEPS=[ + 'auth', + 'address_restriction', '$BUILD_DIR/mongo/base', '$BUILD_DIR/mongo/db/common', ] @@ -121,26 +160,12 @@ env.Library('authorization_manager_global', 'authorization_manager_global.cpp', ], LIBDEPS=[ - 'authcore', + 'auth', '$BUILD_DIR/mongo/db/server_options_core', '$BUILD_DIR/mongo/db/service_context', ]) env.Library( - target='authorization_manager_mock_init', - source=[ - 'authorization_manager_mock_init.cpp' - ], - LIBDEPS=[ - 'authcore', - 'authmocks', - '$BUILD_DIR/mongo/executor/thread_pool_task_executor', - '$BUILD_DIR/mongo/executor/network_interface_thread_pool', - '$BUILD_DIR/mongo/executor/network_interface_factory' - ], -) - -env.Library( target='authservercommon', source=[ 'authz_session_external_state_server_common.cpp', @@ -148,8 +173,9 @@ env.Library( 'security_key.cpp', ], LIBDEPS=[ + 'auth', 'authcommon', - 'authcore', + 'auth_impl_internal', 'authorization_manager_global', 'saslauth', 'security_file', @@ -187,11 +213,17 @@ env.Library( 'sasl_scram_server_conversation.cpp', ], LIBDEPS=[ - 'authcore', + 'auth', + 'authprivilege', 'sasl_options', + 'user', + 'user_name', + '$BUILD_DIR/mongo/base', '$BUILD_DIR/mongo/base/secure_allocator', + '$BUILD_DIR/mongo/crypto/sha_block_${MONGO_CRYPTO}', '$BUILD_DIR/mongo/db/commands/test_commands_enabled', '$BUILD_DIR/mongo/util/icu', + '$BUILD_DIR/mongo/util/md5', '$BUILD_DIR/mongo/util/net/network', ], ) @@ -209,10 +241,10 @@ env.CppUnitTest(target='sasl_mechanism_registry_test', env.Library('authmongod', ['authz_manager_external_state_d.cpp', 'authz_session_external_state_d.cpp', - 'impersonation_session.cpp' ], LIBDEPS=[ 'authservercommon', + 'auth_impl_internal_local', '$BUILD_DIR/mongo/db/db_raii', '$BUILD_DIR/mongo/db/dbdirectclient', '$BUILD_DIR/mongo/db/dbhelpers', @@ -235,32 +267,35 @@ env.Library('authmongos', env.Library( target='authmocks', source=[ - 'authz_manager_external_state_mock.cpp' + 'authz_manager_external_state_mock.cpp', + 'authz_session_external_state_mock.cpp', ], LIBDEPS=[ '$BUILD_DIR/mongo/db/matcher/expressions', '$BUILD_DIR/mongo/db/service_context', '$BUILD_DIR/mongo/db/update/update_driver', - 'authcore' + 'auth', + 'auth_impl_internal', + 'auth_impl_internal_local', ] ) env.CppUnitTest('action_set_test', 'action_set_test.cpp', - LIBDEPS=['authcore', 'authmocks']) + LIBDEPS=['auth', 'authmocks']) env.CppUnitTest('privilege_parser_test', 'privilege_parser_test.cpp', - LIBDEPS=['authcore', 'authmocks']) + LIBDEPS=['auth', 'authmocks']) env.CppUnitTest('role_graph_test', 'role_graph_test.cpp', - LIBDEPS=['authcore', 'authmocks']) + LIBDEPS=['auth', 'authmocks']) env.CppUnitTest('user_document_parser_test', 'user_document_parser_test.cpp', - LIBDEPS=['authcore', 'authmocks']) + LIBDEPS=['auth', 'authmocks']) env.CppUnitTest('user_set_test', 'user_set_test.cpp', - LIBDEPS=['authcore', 'authmocks']) + LIBDEPS=['auth', 'authmocks']) env.CppUnitTest('authorization_manager_test', 'authorization_manager_test.cpp', LIBDEPS=[ '$BUILD_DIR/mongo/transport/transport_layer_common', '$BUILD_DIR/mongo/transport/transport_layer_mock', - 'authcore', - 'authmocks' + 'auth', + 'authmocks', ]) env.Library( @@ -269,7 +304,8 @@ env.Library( 'authorization_session_for_test.cpp', ], LIBDEPS=[ - 'authcore', + 'auth', + 'auth_impl_internal', ] ) @@ -279,7 +315,7 @@ env.CppUnitTest( 'authorization_session_test.cpp', ], LIBDEPS=[ - 'authcore', + 'auth', 'authmocks', 'saslauth', 'authorization_session_for_test', @@ -322,6 +358,7 @@ env.CppUnitTest( LIBDEPS_PRIVATE=[ 'authmocks', 'saslauth', + 'authmocks', '$BUILD_DIR/mongo/client/sasl_client', '$BUILD_DIR/mongo/db/service_context_noop_init', ], diff --git a/src/mongo/db/auth/auth_decorations.cpp b/src/mongo/db/auth/auth_decorations.cpp index b0c1e79f4a9..c78117b91e6 100644 --- a/src/mongo/db/auth/auth_decorations.cpp +++ b/src/mongo/db/auth/auth_decorations.cpp @@ -36,7 +36,6 @@ #include "mongo/db/auth/authorization_session.h" #include "mongo/db/client.h" #include "mongo/db/service_context.h" -#include "mongo/stdx/memory.h" #include "mongo/util/assert_util.h" namespace mongo { @@ -87,7 +86,7 @@ AuthorizationManager* AuthorizationManager::get(ServiceContext& service) { void AuthorizationManager::set(ServiceContext* service, std::unique_ptr<AuthorizationManager> authzManager) { getAuthorizationManager(service) = std::move(authzManager); - service->registerClientObserver(stdx::make_unique<AuthzClientObserver>()); + service->registerClientObserver(std::make_unique<AuthzClientObserver>()); } AuthorizationSession* AuthorizationSession::get(Client* client) { diff --git a/src/mongo/db/auth/authorization_manager.cpp b/src/mongo/db/auth/authorization_manager.cpp index 0b0bedd461e..e20c771fe6e 100644 --- a/src/mongo/db/auth/authorization_manager.cpp +++ b/src/mongo/db/auth/authorization_manager.cpp @@ -1,30 +1,30 @@ /** -* Copyright (C) 2012 10gen 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. -*/ + * Copyright (C) 2018 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. + */ #define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kAccessControl @@ -64,48 +64,9 @@ #include "mongo/util/log.h" #include "mongo/util/mongoutils/str.h" -namespace mongo { - -using std::begin; -using std::end; -using std::endl; -using std::back_inserter; -using std::string; -using std::vector; - -AuthInfo internalSecurity; - -MONGO_INITIALIZER_WITH_PREREQUISITES(SetupInternalSecurityUser, ("EndStartupOptionStorage")) -(InitializerContext* const context) try { - User* user = new User(UserName("__system", "local")); - - user->incrementRefCount(); // Pin this user so the ref count never drops below 1. - ActionSet allActions; - allActions.addAllActions(); - PrivilegeVector privileges; - RoleGraph::generateUniversalPrivileges(&privileges); - user->addPrivileges(privileges); - - if (mongodGlobalParams.whitelistedClusterNetwork) { - const auto& whitelist = *mongodGlobalParams.whitelistedClusterNetwork; - - auto restriction = stdx::make_unique<ClientSourceRestriction>(whitelist); - auto restrictionSet = stdx::make_unique<RestrictionSet<>>(std::move(restriction)); - auto restrictionDocument = - stdx::make_unique<RestrictionDocument<>>(std::move(restrictionSet)); - - RestrictionDocuments clusterWhiteList(std::move(restrictionDocument)); +mongo::AuthInfo mongo::internalSecurity; - user->setRestrictions(std::move(clusterWhiteList)); - } - - - internalSecurity.user = user; - - return Status::OK(); -} catch (...) { - return exceptionToStatus(); -} +namespace mongo { const std::string AuthorizationManager::USER_NAME_FIELD_NAME = "user"; const std::string AuthorizationManager::USER_DB_FIELD_NAME = "db"; @@ -138,643 +99,6 @@ const int AuthorizationManager::schemaVersion26Upgrade; const int AuthorizationManager::schemaVersion26Final; const int AuthorizationManager::schemaVersion28SCRAM; -/** - * Guard object for synchronizing accesses to data cached in AuthorizationManager instances. - * This guard allows one thread to access the cache at a time, and provides an exception-safe - * mechanism for a thread to release the cache mutex while performing network or disk operations - * while allowing other readers to proceed. - * - * There are two ways to use this guard. One may simply instantiate the guard like a - * std::lock_guard, and perform reads or writes of the cache. - * - * Alternatively, one may instantiate the guard, examine the cache, and then enter into an - * update mode by first wait()ing until otherUpdateInFetchPhase() is false, and then - * calling beginFetchPhase(). At this point, other threads may acquire the guard in the simple - * manner and do reads, but other threads may not enter into a fetch phase. During the fetch - * phase, the thread should perform required network or disk activity to determine what update - * it will make to the cache. Then, it should call endFetchPhase(), to reacquire the user cache - * mutex. At that point, the thread can make its modifications to the cache and let the guard - * go out of scope. - * - * All updates by guards using a fetch-phase are totally ordered with respect to one another, - * and all guards using no fetch phase are totally ordered with respect to one another, but - * there is not a total ordering among all guard objects. - * - * The cached data has an associated counter, called the cache generation. If the cache - * generation changes while a guard is in fetch phase, the fetched data should not be stored - * into the cache, because some invalidation event occurred during the fetch phase. - * - * NOTE: It is not safe to enter fetch phase while holding a database lock. Fetch phase - * operations are allowed to acquire database locks themselves, so entering fetch while holding - * a database lock may lead to deadlock. - */ -class AuthorizationManager::CacheGuard { - MONGO_DISALLOW_COPYING(CacheGuard); - -public: - enum FetchSynchronization { fetchSynchronizationAutomatic, fetchSynchronizationManual }; - - /** - * Constructs a cache guard, locking the mutex that synchronizes user cache accesses. - */ - CacheGuard(AuthorizationManager* authzManager, - const FetchSynchronization sync = fetchSynchronizationAutomatic) - : _isThisGuardInFetchPhase(false), - _authzManager(authzManager), - _lock(authzManager->_cacheMutex) { - if (fetchSynchronizationAutomatic == sync) { - synchronizeWithFetchPhase(); - } - } - - /** - * Releases the mutex that synchronizes user cache access, if held, and notifies - * any threads waiting for their own opportunity to update the user cache. - */ - ~CacheGuard() { - if (!_lock.owns_lock()) { - _lock.lock(); - } - if (_isThisGuardInFetchPhase) { - fassert(17190, _authzManager->_isFetchPhaseBusy); - _authzManager->_isFetchPhaseBusy = false; - _authzManager->_fetchPhaseIsReady.notify_all(); - } - } - - /** - * Returns true of the authzManager reports that it is in fetch phase. - */ - bool otherUpdateInFetchPhase() { - return _authzManager->_isFetchPhaseBusy; - } - - /** - * Waits on the _authzManager->_fetchPhaseIsReady condition. - */ - void wait() { - fassert(17222, !_isThisGuardInFetchPhase); - _authzManager->_fetchPhaseIsReady.wait(_lock); - } - - /** - * Enters fetch phase, releasing the _authzManager->_cacheMutex after recording the current - * cache generation. - */ - void beginFetchPhase() { - fassert(17191, !_authzManager->_isFetchPhaseBusy); - _isThisGuardInFetchPhase = true; - _authzManager->_isFetchPhaseBusy = true; - _startGeneration = _authzManager->_cacheGeneration; - _lock.unlock(); - } - - /** - * Exits the fetch phase, reacquiring the _authzManager->_cacheMutex. - */ - void endFetchPhase() { - _lock.lock(); - // We do not clear _authzManager->_isFetchPhaseBusy or notify waiters until - // ~CacheGuard(), for two reasons. First, there's no value to notifying the waiters - // before you're ready to release the mutex, because they'll just go to sleep on the - // mutex. Second, in order to meaningfully check the preconditions of - // isSameCacheGeneration(), we need a state that means "fetch phase was entered and now - // has been exited." That state is _isThisGuardInFetchPhase == true and - // _lock.owns_lock() == true. - } - - /** - * Returns true if _authzManager->_cacheGeneration remained the same while this guard was - * in fetch phase. Behavior is undefined if this guard never entered fetch phase. - * - * If this returns true, do not update the cached data with this - */ - bool isSameCacheGeneration() const { - fassert(17223, _isThisGuardInFetchPhase); - fassert(17231, _lock.owns_lock()); - return _startGeneration == _authzManager->_cacheGeneration; - } - -private: - void synchronizeWithFetchPhase() { - while (otherUpdateInFetchPhase()) - wait(); - fassert(17192, !_authzManager->_isFetchPhaseBusy); - _isThisGuardInFetchPhase = true; - _authzManager->_isFetchPhaseBusy = true; - } - - OID _startGeneration; - bool _isThisGuardInFetchPhase; - AuthorizationManager* _authzManager; - stdx::unique_lock<stdx::mutex> _lock; -}; - -AuthorizationManager::AuthorizationManager(std::unique_ptr<AuthzManagerExternalState> externalState) - : _authEnabled(false), - _privilegeDocsExist(false), - _externalState(std::move(externalState)), - _version(schemaVersionInvalid), - _isFetchPhaseBusy(false) { - _updateCacheGeneration_inlock(); -} - -AuthorizationManager::~AuthorizationManager() { - for (stdx::unordered_map<UserName, User*>::iterator it = _userCache.begin(); - it != _userCache.end(); - ++it) { - fassert(17265, it->second != internalSecurity.user); - delete it->second; - } -} - -std::unique_ptr<AuthorizationSession> AuthorizationManager::makeAuthorizationSession() { - return stdx::make_unique<AuthorizationSession>( - _externalState->makeAuthzSessionExternalState(this)); -} - -void AuthorizationManager::setShouldValidateAuthSchemaOnStartup(bool validate) { - _startupAuthSchemaValidation = validate; -} - -bool AuthorizationManager::shouldValidateAuthSchemaOnStartup() { - return _startupAuthSchemaValidation; -} - -Status AuthorizationManager::getAuthorizationVersion(OperationContext* opCtx, int* version) { - CacheGuard guard(this, CacheGuard::fetchSynchronizationManual); - int newVersion = _version; - if (schemaVersionInvalid == newVersion) { - while (guard.otherUpdateInFetchPhase()) - guard.wait(); - guard.beginFetchPhase(); - Status status = _externalState->getStoredAuthorizationVersion(opCtx, &newVersion); - guard.endFetchPhase(); - if (!status.isOK()) { - warning() << "Problem fetching the stored schema version of authorization data: " - << redact(status); - *version = schemaVersionInvalid; - return status; - } - - if (guard.isSameCacheGeneration()) { - _version = newVersion; - } - } - *version = newVersion; - return Status::OK(); -} - -OID AuthorizationManager::getCacheGeneration() { - CacheGuard guard(this, CacheGuard::fetchSynchronizationManual); - return _cacheGeneration; -} - -void AuthorizationManager::setAuthEnabled(bool enabled) { - _authEnabled = enabled; -} - -bool AuthorizationManager::isAuthEnabled() const { - return _authEnabled; -} - -bool AuthorizationManager::hasAnyPrivilegeDocuments(OperationContext* opCtx) { - stdx::unique_lock<stdx::mutex> lk(_privilegeDocsExistMutex); - if (_privilegeDocsExist) { - // If we know that a user exists, don't re-check. - return true; - } - - lk.unlock(); - bool privDocsExist = _externalState->hasAnyPrivilegeDocuments(opCtx); - lk.lock(); - - if (privDocsExist) { - _privilegeDocsExist = true; - } - - return _privilegeDocsExist; -} - -Status AuthorizationManager::getBSONForPrivileges(const PrivilegeVector& privileges, - mutablebson::Element resultArray) { - for (PrivilegeVector::const_iterator it = privileges.begin(); it != privileges.end(); ++it) { - std::string errmsg; - ParsedPrivilege privilege; - if (!ParsedPrivilege::privilegeToParsedPrivilege(*it, &privilege, &errmsg)) { - return Status(ErrorCodes::BadValue, errmsg); - } - resultArray.appendObject("privileges", privilege.toBSON()).transitional_ignore(); - } - return Status::OK(); -} - -Status AuthorizationManager::getBSONForRole(RoleGraph* graph, - const RoleName& roleName, - mutablebson::Element result) { - if (!graph->roleExists(roleName)) { - return Status(ErrorCodes::RoleNotFound, - mongoutils::str::stream() << roleName.getFullName() - << "does not name an existing role"); - } - std::string id = mongoutils::str::stream() << roleName.getDB() << "." << roleName.getRole(); - result.appendString("_id", id).transitional_ignore(); - result.appendString(ROLE_NAME_FIELD_NAME, roleName.getRole()).transitional_ignore(); - result.appendString(ROLE_DB_FIELD_NAME, roleName.getDB()).transitional_ignore(); - - // Build privileges array - mutablebson::Element privilegesArrayElement = - result.getDocument().makeElementArray("privileges"); - result.pushBack(privilegesArrayElement).transitional_ignore(); - const PrivilegeVector& privileges = graph->getDirectPrivileges(roleName); - Status status = getBSONForPrivileges(privileges, privilegesArrayElement); - if (!status.isOK()) { - return status; - } - - // Build roles array - mutablebson::Element rolesArrayElement = result.getDocument().makeElementArray("roles"); - result.pushBack(rolesArrayElement).transitional_ignore(); - for (RoleNameIterator roles = graph->getDirectSubordinates(roleName); roles.more(); - roles.next()) { - const RoleName& subRole = roles.get(); - mutablebson::Element roleObj = result.getDocument().makeElementObject(""); - roleObj.appendString(ROLE_NAME_FIELD_NAME, subRole.getRole()).transitional_ignore(); - roleObj.appendString(ROLE_DB_FIELD_NAME, subRole.getDB()).transitional_ignore(); - rolesArrayElement.pushBack(roleObj).transitional_ignore(); - } - - return Status::OK(); -} - -Status AuthorizationManager::_initializeUserFromPrivilegeDocument(User* user, - const BSONObj& privDoc) { - V2UserDocumentParser parser; - std::string userName = parser.extractUserNameFromUserDocument(privDoc); - if (userName != user->getName().getUser()) { - return Status(ErrorCodes::BadValue, - mongoutils::str::stream() << "User name from privilege document \"" - << userName - << "\" doesn't match name of provided User \"" - << user->getName().getUser() - << "\""); - } - - Status status = parser.initializeUserCredentialsFromUserDocument(user, privDoc); - if (!status.isOK()) { - return status; - } - status = parser.initializeUserRolesFromUserDocument(privDoc, user); - if (!status.isOK()) { - return status; - } - status = parser.initializeUserIndirectRolesFromUserDocument(privDoc, user); - if (!status.isOK()) { - return status; - } - status = parser.initializeUserPrivilegesFromUserDocument(privDoc, user); - if (!status.isOK()) { - return status; - } - status = parser.initializeAuthenticationRestrictionsFromUserDocument(privDoc, user); - if (!status.isOK()) { - return status; - } - - return Status::OK(); -} - -Status AuthorizationManager::getUserDescription(OperationContext* opCtx, - const UserName& userName, - BSONObj* result) { - return _externalState->getUserDescription(opCtx, userName, result); -} - -Status AuthorizationManager::getRoleDescription(OperationContext* opCtx, - const RoleName& roleName, - PrivilegeFormat privileges, - AuthenticationRestrictionsFormat restrictions, - BSONObj* result) { - return _externalState->getRoleDescription(opCtx, roleName, privileges, restrictions, result); -} - -Status AuthorizationManager::getRolesDescription(OperationContext* opCtx, - const std::vector<RoleName>& roleName, - PrivilegeFormat privileges, - AuthenticationRestrictionsFormat restrictions, - BSONObj* result) { - return _externalState->getRolesDescription(opCtx, roleName, privileges, restrictions, result); -} - - -Status AuthorizationManager::getRoleDescriptionsForDB(OperationContext* opCtx, - const std::string dbname, - PrivilegeFormat privileges, - AuthenticationRestrictionsFormat restrictions, - bool showBuiltinRoles, - vector<BSONObj>* result) { - return _externalState->getRoleDescriptionsForDB( - opCtx, dbname, privileges, restrictions, showBuiltinRoles, result); -} - -Status AuthorizationManager::acquireUser(OperationContext* opCtx, - const UserName& userName, - User** acquiredUser) { - if (userName == internalSecurity.user->getName()) { - *acquiredUser = internalSecurity.user; - return Status::OK(); - } - - stdx::unordered_map<UserName, User*>::iterator it; - - CacheGuard guard(this, CacheGuard::fetchSynchronizationManual); - while ((_userCache.end() == (it = _userCache.find(userName))) && - guard.otherUpdateInFetchPhase()) { - guard.wait(); - } - - if (it != _userCache.end()) { - fassert(16914, it->second); - fassert(17003, it->second->isValid()); - fassert(17008, it->second->getRefCount() > 0); - it->second->incrementRefCount(); - *acquiredUser = it->second; - return Status::OK(); - } - - std::unique_ptr<User> user; - - int authzVersion = _version; - guard.beginFetchPhase(); - - // Number of times to retry a user document that fetches due to transient - // AuthSchemaIncompatible errors. These errors should only ever occur during and shortly - // after schema upgrades. - static const int maxAcquireRetries = 2; - Status status = Status::OK(); - for (int i = 0; i < maxAcquireRetries; ++i) { - if (authzVersion == schemaVersionInvalid) { - Status status = _externalState->getStoredAuthorizationVersion(opCtx, &authzVersion); - if (!status.isOK()) - return status; - } - - switch (authzVersion) { - default: - status = Status(ErrorCodes::BadValue, - mongoutils::str::stream() - << "Illegal value for authorization data schema version, " - << authzVersion); - break; - case schemaVersion28SCRAM: - case schemaVersion26Final: - case schemaVersion26Upgrade: - status = _fetchUserV2(opCtx, userName, &user); - break; - case schemaVersion24: - status = Status(ErrorCodes::AuthSchemaIncompatible, - mongoutils::str::stream() - << "Authorization data schema version " - << schemaVersion24 - << " not supported after MongoDB version 2.6."); - break; - } - if (status.isOK()) - break; - if (status != ErrorCodes::AuthSchemaIncompatible) - return status; - - authzVersion = schemaVersionInvalid; - } - if (!status.isOK()) - return status; - - guard.endFetchPhase(); - - user->incrementRefCount(); - // NOTE: It is not safe to throw an exception from here to the end of the method. - if (guard.isSameCacheGeneration()) { - _userCache.insert(std::make_pair(userName, user.get())); - if (_version == schemaVersionInvalid) - _version = authzVersion; - } else { - // If the cache generation changed while this thread was in fetch mode, the data - // associated with the user may now be invalid, so we must mark it as such. The caller - // may still opt to use the information for a short while, but not indefinitely. - user->invalidate(); - } - *acquiredUser = user.release(); - - return Status::OK(); -} - -Status AuthorizationManager::_fetchUserV2(OperationContext* opCtx, - const UserName& userName, - std::unique_ptr<User>* acquiredUser) { - BSONObj userObj; - Status status = getUserDescription(opCtx, userName, &userObj); - if (!status.isOK()) { - return status; - } - - // Put the new user into an unique_ptr temporarily in case there's an error while - // initializing the user. - auto user = stdx::make_unique<User>(userName); - - status = _initializeUserFromPrivilegeDocument(user.get(), userObj); - if (!status.isOK()) { - return status; - } - acquiredUser->reset(user.release()); - return Status::OK(); -} - -void AuthorizationManager::releaseUser(User* user) { - if (user == internalSecurity.user) { - return; - } - - CacheGuard guard(this, CacheGuard::fetchSynchronizationManual); - user->decrementRefCount(); - if (user->getRefCount() == 0) { - // If it's been invalidated then it's not in the _userCache anymore. - if (user->isValid()) { - MONGO_COMPILER_VARIABLE_UNUSED bool erased = _userCache.erase(user->getName()); - dassert(erased); - } - delete user; - } -} - -void AuthorizationManager::invalidateUserByName(const UserName& userName) { - CacheGuard guard(this, CacheGuard::fetchSynchronizationManual); - _updateCacheGeneration_inlock(); - stdx::unordered_map<UserName, User*>::iterator it = _userCache.find(userName); - if (it == _userCache.end()) { - return; - } - - User* user = it->second; - _userCache.erase(it); - user->invalidate(); -} - -void AuthorizationManager::invalidateUsersFromDB(const std::string& dbname) { - CacheGuard guard(this, CacheGuard::fetchSynchronizationManual); - _updateCacheGeneration_inlock(); - stdx::unordered_map<UserName, User*>::iterator it = _userCache.begin(); - while (it != _userCache.end()) { - User* user = it->second; - if (user->getName().getDB() == dbname) { - _userCache.erase(it++); - user->invalidate(); - } else { - ++it; - } - } -} - -void AuthorizationManager::invalidateUserCache() { - CacheGuard guard(this, CacheGuard::fetchSynchronizationManual); - _invalidateUserCache_inlock(); -} - -void AuthorizationManager::_invalidateUserCache_inlock() { - _updateCacheGeneration_inlock(); - for (stdx::unordered_map<UserName, User*>::iterator it = _userCache.begin(); - it != _userCache.end(); - ++it) { - fassert(17266, it->second != internalSecurity.user); - it->second->invalidate(); - } - _userCache.clear(); - - // Reread the schema version before acquiring the next user. - _version = schemaVersionInvalid; -} - -Status AuthorizationManager::initialize(OperationContext* opCtx) { - invalidateUserCache(); - Status status = _externalState->initialize(opCtx); - if (!status.isOK()) - return status; - - return Status::OK(); -} - -namespace { -bool isAuthzNamespace(const NamespaceString& nss) { - return (nss == AuthorizationManager::rolesCollectionNamespace || - nss == AuthorizationManager::usersCollectionNamespace || - nss == AuthorizationManager::versionCollectionNamespace); -} - -bool isAuthzCollection(StringData coll) { - return (coll == AuthorizationManager::rolesCollectionNamespace.coll() || - coll == AuthorizationManager::usersCollectionNamespace.coll() || - coll == AuthorizationManager::versionCollectionNamespace.coll()); -} - -bool loggedCommandOperatesOnAuthzData(const NamespaceString& nss, const BSONObj& cmdObj) { - if (nss != AuthorizationManager::adminCommandNamespace) - return false; - const StringData cmdName(cmdObj.firstElement().fieldNameStringData()); - if (cmdName == "drop") { - return isAuthzCollection(cmdObj.firstElement().valueStringData()); - } else if (cmdName == "dropDatabase") { - return true; - } else if (cmdName == "renameCollection") { - return isAuthzCollection(cmdObj.firstElement().str()) || - isAuthzCollection(cmdObj["to"].str()); - } else if (cmdName == "dropIndexes" || cmdName == "deleteIndexes") { - return false; - } else if (cmdName == "create") { - return false; - } else { - return true; - } -} - -bool appliesToAuthzData(const char* op, const NamespaceString& nss, const BSONObj& o) { - switch (*op) { - case 'i': - case 'u': - case 'd': - if (op[1] != '\0') - return false; // "db" op type - return isAuthzNamespace(nss); - case 'c': - return loggedCommandOperatesOnAuthzData(nss, o); - break; - case 'n': - return false; - default: - return true; - } -} - -// Updates to users in the oplog are done by matching on the _id, which will always have the -// form "<dbname>.<username>". This function extracts the UserName from that string. -StatusWith<UserName> extractUserNameFromIdString(StringData idstr) { - size_t splitPoint = idstr.find('.'); - if (splitPoint == string::npos) { - return StatusWith<UserName>(ErrorCodes::FailedToParse, - mongoutils::str::stream() - << "_id entries for user documents must be of " - "the form <dbname>.<username>. Found: " - << idstr); - } - return StatusWith<UserName>( - UserName(idstr.substr(splitPoint + 1), idstr.substr(0, splitPoint))); -} - -} // namespace - -void AuthorizationManager::_updateCacheGeneration_inlock() { - _cacheGeneration = OID::gen(); -} - -void AuthorizationManager::_invalidateRelevantCacheData(const char* op, - const NamespaceString& ns, - const BSONObj& o, - const BSONObj* o2) { - if (ns == AuthorizationManager::rolesCollectionNamespace || - ns == AuthorizationManager::versionCollectionNamespace) { - invalidateUserCache(); - return; - } - - if (*op == 'i' || *op == 'd' || *op == 'u') { - // If you got into this function isAuthzNamespace() must have returned true, and we've - // already checked that it's not the roles or version collection. - invariant(ns == AuthorizationManager::usersCollectionNamespace); - - StatusWith<UserName> userName = (*op == 'u') - ? extractUserNameFromIdString((*o2)["_id"].str()) - : extractUserNameFromIdString(o["_id"].str()); - - if (!userName.isOK()) { - warning() << "Invalidating user cache based on user being updated failed, will " - "invalidate the entire cache instead: " - << userName.getStatus(); - invalidateUserCache(); - return; - } - invalidateUserByName(userName.getValue()); - } else { - invalidateUserCache(); - } -} - -void AuthorizationManager::logOp(OperationContext* opCtx, - const char* op, - const NamespaceString& nss, - const BSONObj& o, - const BSONObj* o2) { - if (appliesToAuthzData(op, nss, o)) { - _externalState->logOp(opCtx, op, nss, o, o2); - _invalidateRelevantCacheData(op, nss, o, o2); - } -} +MONGO_DEFINE_SHIM(AuthorizationManager::create); } // namespace mongo diff --git a/src/mongo/db/auth/authorization_manager.h b/src/mongo/db/auth/authorization_manager.h index c21d672c688..5d9506a9f18 100644 --- a/src/mongo/db/auth/authorization_manager.h +++ b/src/mongo/db/auth/authorization_manager.h @@ -1,30 +1,30 @@ /** -* Copyright (C) 2013 10gen 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. -*/ + * Copyright (C) 2018 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 @@ -33,6 +33,7 @@ #include "mongo/base/disallow_copying.h" #include "mongo/base/secure_allocator.h" +#include "mongo/base/shim.h" #include "mongo/base/status.h" #include "mongo/bson/mutable/element.h" #include "mongo/bson/oid.h" @@ -79,17 +80,19 @@ enum class AuthenticationRestrictionsFormat { * Contains server/cluster-wide information about Authorization. */ class AuthorizationManager { - MONGO_DISALLOW_COPYING(AuthorizationManager); + AuthorizationManager(const AuthorizationManager&) = delete; + AuthorizationManager& operator=(const AuthorizationManager&) = delete; public: static AuthorizationManager* get(ServiceContext* service); static AuthorizationManager* get(ServiceContext& service); static void set(ServiceContext* service, std::unique_ptr<AuthorizationManager> authzManager); - // The newly constructed AuthorizationManager takes ownership of "externalState" - explicit AuthorizationManager(std::unique_ptr<AuthzManagerExternalState> externalState); + virtual ~AuthorizationManager() = default; + + AuthorizationManager() = default; - ~AuthorizationManager(); + static MONGO_DECLARE_SHIM(()->std::unique_ptr<AuthorizationManager>) create; static const std::string USER_NAME_FIELD_NAME; static const std::string USER_DB_FIELD_NAME; @@ -179,27 +182,27 @@ public: /** * Returns a new AuthorizationSession for use with this AuthorizationManager. */ - std::unique_ptr<AuthorizationSession> makeAuthorizationSession(); + virtual std::unique_ptr<AuthorizationSession> makeAuthorizationSession() = 0; /** * Sets whether or not startup AuthSchema validation checks should be applied in this manager. */ - void setShouldValidateAuthSchemaOnStartup(bool validate); + virtual void setShouldValidateAuthSchemaOnStartup(bool validate) = 0; /** * Returns true if startup AuthSchema validation checks should be applied in this manager. */ - bool shouldValidateAuthSchemaOnStartup(); + virtual bool shouldValidateAuthSchemaOnStartup() = 0; /** * Sets whether or not access control enforcement is enabled for this manager. */ - void setAuthEnabled(bool enabled); + virtual void setAuthEnabled(bool enabled) = 0; /** * Returns true if access control is enabled for this manager . */ - bool isAuthEnabled() const; + virtual bool isAuthEnabled() const = 0; /** * Returns via the output parameter "version" the version number of the authorization @@ -208,12 +211,12 @@ public: * returns a non-OK status. When returning a non-OK status, *version will be set to * schemaVersionInvalid (0). */ - Status getAuthorizationVersion(OperationContext* opCtx, int* version); + virtual Status getAuthorizationVersion(OperationContext* opCtx, int* version) = 0; /** * Returns the user cache generation identifier. */ - OID getCacheGeneration(); + virtual OID getCacheGeneration() = 0; /** * Returns true if there exists at least one privilege document in the system. @@ -223,31 +226,23 @@ public: * meaning that once this method returns true it will continue to return true for the * lifetime of this process, even if all users are subsequently dropped from the system. */ - bool hasAnyPrivilegeDocuments(OperationContext* opCtx); - - // Checks to see if "doc" is a valid privilege document, assuming it is stored in the - // "system.users" collection of database "dbname". - // - // Returns Status::OK() if the document is good, or Status(ErrorCodes::BadValue), otherwise. - Status checkValidPrivilegeDocument(StringData dbname, const BSONObj& doc); - - // Given a database name and a readOnly flag return an ActionSet describing all the actions - // that an old-style user with those attributes should be given. - ActionSet getActionsForOldStyleUser(const std::string& dbname, bool readOnly) const; + virtual bool hasAnyPrivilegeDocuments(OperationContext* opCtx) = 0; /** * Delegates method call to the underlying AuthzManagerExternalState. */ - Status getUserDescription(OperationContext* opCtx, const UserName& userName, BSONObj* result); + virtual Status getUserDescription(OperationContext* opCtx, + const UserName& userName, + BSONObj* result) = 0; /** * Delegates method call to the underlying AuthzManagerExternalState. */ - Status getRoleDescription(OperationContext* opCtx, - const RoleName& roleName, - PrivilegeFormat privilegeFormat, - AuthenticationRestrictionsFormat, - BSONObj* result); + virtual Status getRoleDescription(OperationContext* opCtx, + const RoleName& roleName, + PrivilegeFormat privilegeFormat, + AuthenticationRestrictionsFormat, + BSONObj* result) = 0; /** * Convenience wrapper for getRoleDescription() defaulting formats to kOmit. @@ -260,21 +255,21 @@ public: /** * Delegates method call to the underlying AuthzManagerExternalState. */ - Status getRolesDescription(OperationContext* opCtx, - const std::vector<RoleName>& roleName, - PrivilegeFormat privilegeFormat, - AuthenticationRestrictionsFormat, - BSONObj* result); + virtual Status getRolesDescription(OperationContext* opCtx, + const std::vector<RoleName>& roleName, + PrivilegeFormat privilegeFormat, + AuthenticationRestrictionsFormat, + BSONObj* result) = 0; /** * Delegates method call to the underlying AuthzManagerExternalState. */ - Status getRoleDescriptionsForDB(OperationContext* opCtx, - const std::string dbname, - PrivilegeFormat privilegeFormat, - AuthenticationRestrictionsFormat, - bool showBuiltinRoles, - std::vector<BSONObj>* result); + virtual Status getRoleDescriptionsForDB(OperationContext* opCtx, + const std::string dbname, + PrivilegeFormat privilegeFormat, + AuthenticationRestrictionsFormat, + bool showBuiltinRoles, + std::vector<BSONObj>* result) = 0; /** * Returns the User object for the given userName in the out parameter "acquiredUser". @@ -286,35 +281,37 @@ public: * The AuthorizationManager retains ownership of the returned User object. * On non-OK Status return values, acquiredUser will not be modified. */ - Status acquireUser(OperationContext* opCtx, const UserName& userName, User** acquiredUser); + virtual Status acquireUser(OperationContext* opCtx, + const UserName& userName, + User** acquiredUser) = 0; /** * Decrements the refcount of the given User object. If the refcount has gone to zero, * deletes the User. Caller must stop using its pointer to "user" after calling this. */ - void releaseUser(User* user); + virtual void releaseUser(User* user) = 0; /** * Marks the given user as invalid and removes it from the user cache. */ - void invalidateUserByName(const UserName& user); + virtual void invalidateUserByName(const UserName& user) = 0; /** * Invalidates all users who's source is "dbname" and removes them from the user cache. */ - void invalidateUsersFromDB(const std::string& dbname); + virtual void invalidateUsersFromDB(const std::string& dbname) = 0; /** * Initializes the authorization manager. Depending on what version the authorization * system is at, this may involve building up the user cache and/or the roles graph. * Call this function at startup and after resynchronizing a slave/secondary. */ - Status initialize(OperationContext* opCtx); + virtual Status initialize(OperationContext* opCtx) = 0; /** * Invalidates all of the contents of the user cache. */ - void invalidateUserCache(); + virtual void invalidateUserCache() = 0; /** * Parses privDoc and fully initializes the user object (credentials, roles, and privileges) @@ -322,123 +319,17 @@ public: * This should never be called from outside the AuthorizationManager - the only reason it's * public instead of private is so it can be unit tested. */ - Status _initializeUserFromPrivilegeDocument(User* user, const BSONObj& privDoc); + virtual Status _initializeUserFromPrivilegeDocument(User* user, const BSONObj& privDoc) = 0; /** * Hook called by replication code to let the AuthorizationManager observe changes * to relevant collections. */ - void logOp(OperationContext* opCtx, - const char* opstr, - const NamespaceString& nss, - const BSONObj& obj, - const BSONObj* patt); - -private: - /** - * Type used to guard accesses and updates to the user cache. - */ - class CacheGuard; - friend class AuthorizationManager::CacheGuard; - - /** - * Invalidates all User objects in the cache and removes them from the cache. - * Should only be called when already holding _cacheMutex. - */ - void _invalidateUserCache_inlock(); - - /** - * Given the objects describing an oplog entry that affects authorization data, invalidates - * the portion of the user cache that is affected by that operation. Should only be called - * with oplog entries that have been pre-verified to actually affect authorization data. - */ - void _invalidateRelevantCacheData(const char* op, - const NamespaceString& ns, - const BSONObj& o, - const BSONObj* o2); - - /** - * Updates _cacheGeneration to a new OID - */ - void _updateCacheGeneration_inlock(); - - /** - * Fetches user information from a v2-schema user document for the named user, - * and stores a pointer to a new user object into *acquiredUser on success. - */ - Status _fetchUserV2(OperationContext* opCtx, - const UserName& userName, - std::unique_ptr<User>* acquiredUser); - - /** - * True if AuthSchema startup checks should be applied in this AuthorizationManager. - * - * Defaults to true. Changes to its value are not synchronized, so it should only be set - * at initalization-time. - */ - bool _startupAuthSchemaValidation; - - /** - * True if access control enforcement is enabled in this AuthorizationManager. - * - * Defaults to false. Changes to its value are not synchronized, so it should only be set - * at initalization-time. - */ - bool _authEnabled; - - /** - * A cache of whether there are any users set up for the cluster. - */ - bool _privilegeDocsExist; - - // Protects _privilegeDocsExist - mutable stdx::mutex _privilegeDocsExistMutex; - - std::unique_ptr<AuthzManagerExternalState> _externalState; - - /** - * Cached value of the authorization schema version. - * - * May be set by acquireUser() and getAuthorizationVersion(). Invalidated by - * invalidateUserCache(). - * - * Reads and writes guarded by CacheGuard. - */ - int _version; - - /** - * Caches User objects with information about user privileges, to avoid the need to - * go to disk to read user privilege documents whenever possible. Every User object - * has a reference count - the AuthorizationManager must not delete a User object in the - * cache unless its reference count is zero. - */ - stdx::unordered_map<UserName, User*> _userCache; - - /** - * Current generation of cached data. Updated every time part of the cache gets - * invalidated. Protected by CacheGuard. - */ - OID _cacheGeneration; - - /** - * True if there is an update to the _userCache in progress, and that update is currently in - * the "fetch phase", during which it does not hold the _cacheMutex. - * - * Manipulated via CacheGuard. - */ - bool _isFetchPhaseBusy; - - /** - * Protects _userCache, _cacheGeneration, _version and _isFetchPhaseBusy. Manipulated - * via CacheGuard. - */ - stdx::mutex _cacheMutex; - - /** - * Condition used to signal that it is OK for another CacheGuard to enter a fetch phase. - * Manipulated via CacheGuard. - */ - stdx::condition_variable _fetchPhaseIsReady; + virtual void logOp(OperationContext* opCtx, + const char* opstr, + const NamespaceString& nss, + const BSONObj& obj, + const BSONObj* patt) = 0; }; } // namespace mongo diff --git a/src/mongo/db/auth/authorization_manager_global.cpp b/src/mongo/db/auth/authorization_manager_global.cpp index 9738f7b2932..352981b7574 100644 --- a/src/mongo/db/auth/authorization_manager_global.cpp +++ b/src/mongo/db/auth/authorization_manager_global.cpp @@ -32,6 +32,7 @@ #include "mongo/base/init.h" #include "mongo/db/auth/authorization_manager.h" #include "mongo/db/auth/authorization_manager_global.h" +#include "mongo/db/auth/authorization_manager_impl.h" #include "mongo/db/auth/authz_manager_external_state.h" #include "mongo/db/server_options.h" #include "mongo/db/server_parameters.h" @@ -92,14 +93,9 @@ MONGO_EXPORT_STARTUP_SERVER_PARAMETER(startupAuthSchemaValidation, bool, true); GlobalInitializerRegisterer authorizationManagerInitializer( "CreateAuthorizationManager", - {"SetupInternalSecurityUser", - "OIDGeneration", - "CreateAuthorizationExternalStateFactory", - "EndStartupOptionStorage", - "ServiceContext"}, + {"SetupInternalSecurityUser", "OIDGeneration", "EndStartupOptionStorage", "ServiceContext"}, [](InitializerContext* context) { - auto authzManager = - stdx::make_unique<AuthorizationManager>(AuthzManagerExternalState::create()); + auto authzManager = AuthorizationManager::create(); authzManager->setAuthEnabled(serverGlobalParams.authState == ServerGlobalParams::AuthState::kEnabled); authzManager->setShouldValidateAuthSchemaOnStartup(startupAuthSchemaValidation); diff --git a/src/mongo/db/auth/authorization_manager_impl.cpp b/src/mongo/db/auth/authorization_manager_impl.cpp new file mode 100644 index 00000000000..bc8970484fa --- /dev/null +++ b/src/mongo/db/auth/authorization_manager_impl.cpp @@ -0,0 +1,765 @@ +/** + * Copyright (C) 2018 10gen 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. + */ + +#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kAccessControl + +#include "mongo/platform/basic.h" + +#include "mongo/db/auth/authorization_manager_impl.h" + +#include <memory> +#include <string> +#include <vector> + +#include "mongo/base/init.h" +#include "mongo/base/status.h" +#include "mongo/bson/mutable/document.h" +#include "mongo/bson/mutable/element.h" +#include "mongo/bson/util/bson_extract.h" +#include "mongo/crypto/mechanism_scram.h" +#include "mongo/db/auth/action_set.h" +#include "mongo/db/auth/address_restriction.h" +#include "mongo/db/auth/authorization_session.h" +#include "mongo/db/auth/authorization_session_impl.h" +#include "mongo/db/auth/authz_manager_external_state.h" +#include "mongo/db/auth/privilege.h" +#include "mongo/db/auth/privilege_parser.h" +#include "mongo/db/auth/role_graph.h" +#include "mongo/db/auth/sasl_options.h" +#include "mongo/db/auth/sasl_options.h" +#include "mongo/db/auth/user.h" +#include "mongo/db/auth/user_document_parser.h" +#include "mongo/db/auth/user_name.h" +#include "mongo/db/auth/user_name_hash.h" +#include "mongo/db/global_settings.h" +#include "mongo/db/jsobj.h" +#include "mongo/db/mongod_options.h" +#include "mongo/platform/compiler.h" +#include "mongo/stdx/memory.h" +#include "mongo/stdx/mutex.h" +#include "mongo/stdx/unordered_map.h" +#include "mongo/util/assert_util.h" +#include "mongo/util/log.h" +#include "mongo/util/mongoutils/str.h" + +namespace mongo { +namespace { + +using std::begin; +using std::end; +using std::endl; +using std::back_inserter; +using std::string; +using std::vector; + + +MONGO_INITIALIZER_WITH_PREREQUISITES(SetupInternalSecurityUser, ("EndStartupOptionStorage")) +(InitializerContext* const context) try { + User* user = new User(UserName("__system", "local")); + + user->incrementRefCount(); // Pin this user so the ref count never drops below 1. + ActionSet allActions; + allActions.addAllActions(); + PrivilegeVector privileges; + RoleGraph::generateUniversalPrivileges(&privileges); + user->addPrivileges(privileges); + + if (mongodGlobalParams.whitelistedClusterNetwork) { + const auto& whitelist = *mongodGlobalParams.whitelistedClusterNetwork; + + auto restriction = stdx::make_unique<ClientSourceRestriction>(whitelist); + auto restrictionSet = stdx::make_unique<RestrictionSet<>>(std::move(restriction)); + auto restrictionDocument = + stdx::make_unique<RestrictionDocument<>>(std::move(restrictionSet)); + + RestrictionDocuments clusterWhiteList(std::move(restrictionDocument)); + + user->setRestrictions(std::move(clusterWhiteList)); + } + + + internalSecurity.user = user; + + return Status::OK(); +} catch (...) { + return exceptionToStatus(); +} +} // namespace + + +MONGO_REGISTER_SHIM(AuthorizationManager::create)()->std::unique_ptr<AuthorizationManager> { + return std::make_unique<AuthorizationManagerImpl>(); +} + +/** + * Guard object for synchronizing accesses to data cached in AuthorizationManager instances. + * This guard allows one thread to access the cache at a time, and provides an exception-safe + * mechanism for a thread to release the cache mutex while performing network or disk operations + * while allowing other readers to proceed. + * + * There are two ways to use this guard. One may simply instantiate the guard like a + * std::lock_guard, and perform reads or writes of the cache. + * + * Alternatively, one may instantiate the guard, examine the cache, and then enter into an + * update mode by first wait()ing until otherUpdateInFetchPhase() is false, and then + * calling beginFetchPhase(). At this point, other threads may acquire the guard in the simple + * manner and do reads, but other threads may not enter into a fetch phase. During the fetch + * phase, the thread should perform required network or disk activity to determine what update + * it will make to the cache. Then, it should call endFetchPhase(), to reacquire the user cache + * mutex. At that point, the thread can make its modifications to the cache and let the guard + * go out of scope. + * + * All updates by guards using a fetch-phase are totally ordered with respect to one another, + * and all guards using no fetch phase are totally ordered with respect to one another, but + * there is not a total ordering among all guard objects. + * + * The cached data has an associated counter, called the cache generation. If the cache + * generation changes while a guard is in fetch phase, the fetched data should not be stored + * into the cache, because some invalidation event occurred during the fetch phase. + * + * NOTE: It is not safe to enter fetch phase while holding a database lock. Fetch phase + * operations are allowed to acquire database locks themselves, so entering fetch while holding + * a database lock may lead to deadlock. + */ +class AuthorizationManagerImpl::CacheGuard { + MONGO_DISALLOW_COPYING(CacheGuard); + +public: + enum FetchSynchronization { fetchSynchronizationAutomatic, fetchSynchronizationManual }; + + /** + * Constructs a cache guard, locking the mutex that synchronizes user cache accesses. + */ + CacheGuard(AuthorizationManagerImpl* authzManager, + const FetchSynchronization sync = fetchSynchronizationAutomatic) + : _isThisGuardInFetchPhase(false), + _authzManager(authzManager), + _lock(authzManager->_cacheMutex) { + if (fetchSynchronizationAutomatic == sync) { + synchronizeWithFetchPhase(); + } + } + + /** + * Releases the mutex that synchronizes user cache access, if held, and notifies + * any threads waiting for their own opportunity to update the user cache. + */ + ~CacheGuard() { + if (!_lock.owns_lock()) { + _lock.lock(); + } + if (_isThisGuardInFetchPhase) { + fassert(17190, _authzManager->_isFetchPhaseBusy); + _authzManager->_isFetchPhaseBusy = false; + _authzManager->_fetchPhaseIsReady.notify_all(); + } + } + + /** + * Returns true of the authzManager reports that it is in fetch phase. + */ + bool otherUpdateInFetchPhase() { + return _authzManager->_isFetchPhaseBusy; + } + + /** + * Waits on the _authzManager->_fetchPhaseIsReady condition. + */ + void wait() { + fassert(17222, !_isThisGuardInFetchPhase); + _authzManager->_fetchPhaseIsReady.wait(_lock); + } + + /** + * Enters fetch phase, releasing the _authzManager->_cacheMutex after recording the current + * cache generation. + */ + void beginFetchPhase() { + fassert(17191, !_authzManager->_isFetchPhaseBusy); + _isThisGuardInFetchPhase = true; + _authzManager->_isFetchPhaseBusy = true; + _startGeneration = _authzManager->_cacheGeneration; + _lock.unlock(); + } + + /** + * Exits the fetch phase, reacquiring the _authzManager->_cacheMutex. + */ + void endFetchPhase() { + _lock.lock(); + // We do not clear _authzManager->_isFetchPhaseBusy or notify waiters until + // ~CacheGuard(), for two reasons. First, there's no value to notifying the waiters + // before you're ready to release the mutex, because they'll just go to sleep on the + // mutex. Second, in order to meaningfully check the preconditions of + // isSameCacheGeneration(), we need a state that means "fetch phase was entered and now + // has been exited." That state is _isThisGuardInFetchPhase == true and + // _lock.owns_lock() == true. + } + + /** + * Returns true if _authzManager->_cacheGeneration remained the same while this guard was + * in fetch phase. Behavior is undefined if this guard never entered fetch phase. + * + * If this returns true, do not update the cached data with this + */ + bool isSameCacheGeneration() const { + fassert(17223, _isThisGuardInFetchPhase); + fassert(17231, _lock.owns_lock()); + return _startGeneration == _authzManager->_cacheGeneration; + } + +private: + void synchronizeWithFetchPhase() { + while (otherUpdateInFetchPhase()) + wait(); + fassert(17192, !_authzManager->_isFetchPhaseBusy); + _isThisGuardInFetchPhase = true; + _authzManager->_isFetchPhaseBusy = true; + } + + OID _startGeneration; + bool _isThisGuardInFetchPhase; + AuthorizationManagerImpl* _authzManager; + stdx::unique_lock<stdx::mutex> _lock; +}; + +AuthorizationManagerImpl::AuthorizationManagerImpl() + : AuthorizationManagerImpl(AuthzManagerExternalState::create(), + InstallMockForTestingOrAuthImpl{}) {} + +AuthorizationManagerImpl::AuthorizationManagerImpl( + std::unique_ptr<AuthzManagerExternalState> externalState, InstallMockForTestingOrAuthImpl) + : _authEnabled(false), + _privilegeDocsExist(false), + _externalState(std::move(externalState)), + _version(schemaVersionInvalid), + _isFetchPhaseBusy(false) { + _updateCacheGeneration_inlock(); +} + +AuthorizationManagerImpl::~AuthorizationManagerImpl() { + for (stdx::unordered_map<UserName, User*>::iterator it = _userCache.begin(); + it != _userCache.end(); + ++it) { + fassert(17265, it->second != internalSecurity.user); + delete it->second; + } +} + +std::unique_ptr<AuthorizationSession> AuthorizationManagerImpl::makeAuthorizationSession() { + return std::make_unique<AuthorizationSessionImpl>( + _externalState->makeAuthzSessionExternalState(this), + AuthorizationSessionImpl::InstallMockForTestingOrAuthImpl{}); +} + +void AuthorizationManagerImpl::setShouldValidateAuthSchemaOnStartup(bool validate) { + _startupAuthSchemaValidation = validate; +} + +bool AuthorizationManagerImpl::shouldValidateAuthSchemaOnStartup() { + return _startupAuthSchemaValidation; +} + +Status AuthorizationManagerImpl::getAuthorizationVersion(OperationContext* opCtx, int* version) { + CacheGuard guard(this, CacheGuard::fetchSynchronizationManual); + int newVersion = _version; + if (schemaVersionInvalid == newVersion) { + while (guard.otherUpdateInFetchPhase()) + guard.wait(); + guard.beginFetchPhase(); + Status status = _externalState->getStoredAuthorizationVersion(opCtx, &newVersion); + guard.endFetchPhase(); + if (!status.isOK()) { + warning() << "Problem fetching the stored schema version of authorization data: " + << redact(status); + *version = schemaVersionInvalid; + return status; + } + + if (guard.isSameCacheGeneration()) { + _version = newVersion; + } + } + *version = newVersion; + return Status::OK(); +} + +OID AuthorizationManagerImpl::getCacheGeneration() { + CacheGuard guard(this, CacheGuard::fetchSynchronizationManual); + return _cacheGeneration; +} + +void AuthorizationManagerImpl::setAuthEnabled(bool enabled) { + _authEnabled = enabled; +} + +bool AuthorizationManagerImpl::isAuthEnabled() const { + return _authEnabled; +} + +bool AuthorizationManagerImpl::hasAnyPrivilegeDocuments(OperationContext* opCtx) { + stdx::unique_lock<stdx::mutex> lk(_privilegeDocsExistMutex); + if (_privilegeDocsExist) { + // If we know that a user exists, don't re-check. + return true; + } + + lk.unlock(); + bool privDocsExist = _externalState->hasAnyPrivilegeDocuments(opCtx); + lk.lock(); + + if (privDocsExist) { + _privilegeDocsExist = true; + } + + return _privilegeDocsExist; +} + +Status AuthorizationManager::getBSONForPrivileges(const PrivilegeVector& privileges, + mutablebson::Element resultArray) { + for (PrivilegeVector::const_iterator it = privileges.begin(); it != privileges.end(); ++it) { + std::string errmsg; + ParsedPrivilege privilege; + if (!ParsedPrivilege::privilegeToParsedPrivilege(*it, &privilege, &errmsg)) { + return Status(ErrorCodes::BadValue, errmsg); + } + resultArray.appendObject("privileges", privilege.toBSON()).transitional_ignore(); + } + return Status::OK(); +} + +Status AuthorizationManager::getBSONForRole(RoleGraph* graph, + const RoleName& roleName, + mutablebson::Element result) { + if (!graph->roleExists(roleName)) { + return Status(ErrorCodes::RoleNotFound, + mongoutils::str::stream() << roleName.getFullName() + << "does not name an existing role"); + } + std::string id = mongoutils::str::stream() << roleName.getDB() << "." << roleName.getRole(); + result.appendString("_id", id).transitional_ignore(); + result.appendString(ROLE_NAME_FIELD_NAME, roleName.getRole()).transitional_ignore(); + result.appendString(ROLE_DB_FIELD_NAME, roleName.getDB()).transitional_ignore(); + + // Build privileges array + mutablebson::Element privilegesArrayElement = + result.getDocument().makeElementArray("privileges"); + result.pushBack(privilegesArrayElement).transitional_ignore(); + const PrivilegeVector& privileges = graph->getDirectPrivileges(roleName); + Status status = getBSONForPrivileges(privileges, privilegesArrayElement); + if (!status.isOK()) { + return status; + } + + // Build roles array + mutablebson::Element rolesArrayElement = result.getDocument().makeElementArray("roles"); + result.pushBack(rolesArrayElement).transitional_ignore(); + for (RoleNameIterator roles = graph->getDirectSubordinates(roleName); roles.more(); + roles.next()) { + const RoleName& subRole = roles.get(); + mutablebson::Element roleObj = result.getDocument().makeElementObject(""); + roleObj.appendString(ROLE_NAME_FIELD_NAME, subRole.getRole()).transitional_ignore(); + roleObj.appendString(ROLE_DB_FIELD_NAME, subRole.getDB()).transitional_ignore(); + rolesArrayElement.pushBack(roleObj).transitional_ignore(); + } + + return Status::OK(); +} + +Status AuthorizationManagerImpl::_initializeUserFromPrivilegeDocument(User* user, + const BSONObj& privDoc) { + V2UserDocumentParser parser; + std::string userName = parser.extractUserNameFromUserDocument(privDoc); + if (userName != user->getName().getUser()) { + return Status(ErrorCodes::BadValue, + mongoutils::str::stream() << "User name from privilege document \"" + << userName + << "\" doesn't match name of provided User \"" + << user->getName().getUser() + << "\""); + } + + Status status = parser.initializeUserCredentialsFromUserDocument(user, privDoc); + if (!status.isOK()) { + return status; + } + status = parser.initializeUserRolesFromUserDocument(privDoc, user); + if (!status.isOK()) { + return status; + } + status = parser.initializeUserIndirectRolesFromUserDocument(privDoc, user); + if (!status.isOK()) { + return status; + } + status = parser.initializeUserPrivilegesFromUserDocument(privDoc, user); + if (!status.isOK()) { + return status; + } + status = parser.initializeAuthenticationRestrictionsFromUserDocument(privDoc, user); + if (!status.isOK()) { + return status; + } + + return Status::OK(); +} + +Status AuthorizationManagerImpl::getUserDescription(OperationContext* opCtx, + const UserName& userName, + BSONObj* result) { + return _externalState->getUserDescription(opCtx, userName, result); +} + +Status AuthorizationManagerImpl::getRoleDescription(OperationContext* opCtx, + const RoleName& roleName, + PrivilegeFormat privileges, + AuthenticationRestrictionsFormat restrictions, + BSONObj* result) { + return _externalState->getRoleDescription(opCtx, roleName, privileges, restrictions, result); +} + +Status AuthorizationManagerImpl::getRolesDescription(OperationContext* opCtx, + const std::vector<RoleName>& roleName, + PrivilegeFormat privileges, + AuthenticationRestrictionsFormat restrictions, + BSONObj* result) { + return _externalState->getRolesDescription(opCtx, roleName, privileges, restrictions, result); +} + + +Status AuthorizationManagerImpl::getRoleDescriptionsForDB( + OperationContext* opCtx, + const std::string dbname, + PrivilegeFormat privileges, + AuthenticationRestrictionsFormat restrictions, + bool showBuiltinRoles, + vector<BSONObj>* result) { + return _externalState->getRoleDescriptionsForDB( + opCtx, dbname, privileges, restrictions, showBuiltinRoles, result); +} + +Status AuthorizationManagerImpl::acquireUser(OperationContext* opCtx, + const UserName& userName, + User** acquiredUser) { + if (userName == internalSecurity.user->getName()) { + *acquiredUser = internalSecurity.user; + return Status::OK(); + } + + stdx::unordered_map<UserName, User*>::iterator it; + + CacheGuard guard(this, CacheGuard::fetchSynchronizationManual); + while ((_userCache.end() == (it = _userCache.find(userName))) && + guard.otherUpdateInFetchPhase()) { + guard.wait(); + } + + if (it != _userCache.end()) { + fassert(16914, it->second); + fassert(17003, it->second->isValid()); + fassert(17008, it->second->getRefCount() > 0); + it->second->incrementRefCount(); + *acquiredUser = it->second; + return Status::OK(); + } + + std::unique_ptr<User> user; + + int authzVersion = _version; + guard.beginFetchPhase(); + + // Number of times to retry a user document that fetches due to transient + // AuthSchemaIncompatible errors. These errors should only ever occur during and shortly + // after schema upgrades. + static const int maxAcquireRetries = 2; + Status status = Status::OK(); + for (int i = 0; i < maxAcquireRetries; ++i) { + if (authzVersion == schemaVersionInvalid) { + Status status = _externalState->getStoredAuthorizationVersion(opCtx, &authzVersion); + if (!status.isOK()) + return status; + } + + switch (authzVersion) { + default: + status = Status(ErrorCodes::BadValue, + mongoutils::str::stream() + << "Illegal value for authorization data schema version, " + << authzVersion); + break; + case schemaVersion28SCRAM: + case schemaVersion26Final: + case schemaVersion26Upgrade: + status = _fetchUserV2(opCtx, userName, &user); + break; + case schemaVersion24: + status = Status(ErrorCodes::AuthSchemaIncompatible, + mongoutils::str::stream() + << "Authorization data schema version " + << schemaVersion24 + << " not supported after MongoDB version 2.6."); + break; + } + if (status.isOK()) + break; + if (status != ErrorCodes::AuthSchemaIncompatible) + return status; + + authzVersion = schemaVersionInvalid; + } + if (!status.isOK()) + return status; + + guard.endFetchPhase(); + + user->incrementRefCount(); + // NOTE: It is not safe to throw an exception from here to the end of the method. + if (guard.isSameCacheGeneration()) { + _userCache.insert(std::make_pair(userName, user.get())); + if (_version == schemaVersionInvalid) + _version = authzVersion; + } else { + // If the cache generation changed while this thread was in fetch mode, the data + // associated with the user may now be invalid, so we must mark it as such. The caller + // may still opt to use the information for a short while, but not indefinitely. + user->invalidate(); + } + *acquiredUser = user.release(); + + return Status::OK(); +} + +Status AuthorizationManagerImpl::_fetchUserV2(OperationContext* opCtx, + const UserName& userName, + std::unique_ptr<User>* acquiredUser) { + BSONObj userObj; + Status status = getUserDescription(opCtx, userName, &userObj); + if (!status.isOK()) { + return status; + } + + // Put the new user into an unique_ptr temporarily in case there's an error while + // initializing the user. + auto user = stdx::make_unique<User>(userName); + + status = _initializeUserFromPrivilegeDocument(user.get(), userObj); + if (!status.isOK()) { + return status; + } + acquiredUser->reset(user.release()); + return Status::OK(); +} + +void AuthorizationManagerImpl::releaseUser(User* user) { + if (user == internalSecurity.user) { + return; + } + + CacheGuard guard(this, CacheGuard::fetchSynchronizationManual); + user->decrementRefCount(); + if (user->getRefCount() == 0) { + // If it's been invalidated then it's not in the _userCache anymore. + if (user->isValid()) { + MONGO_COMPILER_VARIABLE_UNUSED bool erased = _userCache.erase(user->getName()); + dassert(erased); + } + delete user; + } +} + +void AuthorizationManagerImpl::invalidateUserByName(const UserName& userName) { + CacheGuard guard(this, CacheGuard::fetchSynchronizationManual); + _updateCacheGeneration_inlock(); + stdx::unordered_map<UserName, User*>::iterator it = _userCache.find(userName); + if (it == _userCache.end()) { + return; + } + + User* user = it->second; + _userCache.erase(it); + user->invalidate(); +} + +void AuthorizationManagerImpl::invalidateUsersFromDB(const std::string& dbname) { + CacheGuard guard(this, CacheGuard::fetchSynchronizationManual); + _updateCacheGeneration_inlock(); + stdx::unordered_map<UserName, User*>::iterator it = _userCache.begin(); + while (it != _userCache.end()) { + User* user = it->second; + if (user->getName().getDB() == dbname) { + _userCache.erase(it++); + user->invalidate(); + } else { + ++it; + } + } +} + +void AuthorizationManagerImpl::invalidateUserCache() { + CacheGuard guard(this, CacheGuard::fetchSynchronizationManual); + _invalidateUserCache_inlock(); +} + +void AuthorizationManagerImpl::_invalidateUserCache_inlock() { + _updateCacheGeneration_inlock(); + for (stdx::unordered_map<UserName, User*>::iterator it = _userCache.begin(); + it != _userCache.end(); + ++it) { + fassert(17266, it->second != internalSecurity.user); + it->second->invalidate(); + } + _userCache.clear(); + + // Reread the schema version before acquiring the next user. + _version = schemaVersionInvalid; +} + +Status AuthorizationManagerImpl::initialize(OperationContext* opCtx) { + invalidateUserCache(); + Status status = _externalState->initialize(opCtx); + if (!status.isOK()) + return status; + + return Status::OK(); +} + +namespace { +bool isAuthzNamespace(const NamespaceString& nss) { + return (nss == AuthorizationManager::rolesCollectionNamespace || + nss == AuthorizationManager::usersCollectionNamespace || + nss == AuthorizationManager::versionCollectionNamespace); +} + +bool isAuthzCollection(StringData coll) { + return (coll == AuthorizationManager::rolesCollectionNamespace.coll() || + coll == AuthorizationManager::usersCollectionNamespace.coll() || + coll == AuthorizationManager::versionCollectionNamespace.coll()); +} + +bool loggedCommandOperatesOnAuthzData(const NamespaceString& nss, const BSONObj& cmdObj) { + if (nss != AuthorizationManager::adminCommandNamespace) + return false; + const StringData cmdName(cmdObj.firstElement().fieldNameStringData()); + if (cmdName == "drop") { + return isAuthzCollection(cmdObj.firstElement().valueStringData()); + } else if (cmdName == "dropDatabase") { + return true; + } else if (cmdName == "renameCollection") { + return isAuthzCollection(cmdObj.firstElement().str()) || + isAuthzCollection(cmdObj["to"].str()); + } else if (cmdName == "dropIndexes" || cmdName == "deleteIndexes") { + return false; + } else if (cmdName == "create") { + return false; + } else { + return true; + } +} + +bool appliesToAuthzData(const char* op, const NamespaceString& nss, const BSONObj& o) { + switch (*op) { + case 'i': + case 'u': + case 'd': + if (op[1] != '\0') + return false; // "db" op type + return isAuthzNamespace(nss); + case 'c': + return loggedCommandOperatesOnAuthzData(nss, o); + break; + case 'n': + return false; + default: + return true; + } +} + +// Updates to users in the oplog are done by matching on the _id, which will always have the +// form "<dbname>.<username>". This function extracts the UserName from that string. +StatusWith<UserName> extractUserNameFromIdString(StringData idstr) { + size_t splitPoint = idstr.find('.'); + if (splitPoint == string::npos) { + return StatusWith<UserName>(ErrorCodes::FailedToParse, + mongoutils::str::stream() + << "_id entries for user documents must be of " + "the form <dbname>.<username>. Found: " + << idstr); + } + return StatusWith<UserName>( + UserName(idstr.substr(splitPoint + 1), idstr.substr(0, splitPoint))); +} + +} // namespace + +void AuthorizationManagerImpl::_updateCacheGeneration_inlock() { + _cacheGeneration = OID::gen(); +} + +void AuthorizationManagerImpl::_invalidateRelevantCacheData(const char* op, + const NamespaceString& ns, + const BSONObj& o, + const BSONObj* o2) { + if (ns == AuthorizationManager::rolesCollectionNamespace || + ns == AuthorizationManager::versionCollectionNamespace) { + invalidateUserCache(); + return; + } + + if (*op == 'i' || *op == 'd' || *op == 'u') { + // If you got into this function isAuthzNamespace() must have returned true, and we've + // already checked that it's not the roles or version collection. + invariant(ns == AuthorizationManager::usersCollectionNamespace); + + StatusWith<UserName> userName = (*op == 'u') + ? extractUserNameFromIdString((*o2)["_id"].str()) + : extractUserNameFromIdString(o["_id"].str()); + + if (!userName.isOK()) { + warning() << "Invalidating user cache based on user being updated failed, will " + "invalidate the entire cache instead: " + << userName.getStatus(); + invalidateUserCache(); + return; + } + invalidateUserByName(userName.getValue()); + } else { + invalidateUserCache(); + } +} + +void AuthorizationManagerImpl::logOp(OperationContext* opCtx, + const char* op, + const NamespaceString& nss, + const BSONObj& o, + const BSONObj* o2) { + if (appliesToAuthzData(op, nss, o)) { + _externalState->logOp(opCtx, op, nss, o, o2); + _invalidateRelevantCacheData(op, nss, o, o2); + } +} + +} // namespace mongo diff --git a/src/mongo/db/auth/authorization_manager_impl.h b/src/mongo/db/auth/authorization_manager_impl.h new file mode 100644 index 00000000000..09f3b4ae885 --- /dev/null +++ b/src/mongo/db/auth/authorization_manager_impl.h @@ -0,0 +1,246 @@ +/** + * Copyright (C) 2018 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/auth/authorization_manager.h" + +#include <memory> +#include <string> + +#include "mongo/base/disallow_copying.h" +#include "mongo/base/secure_allocator.h" +#include "mongo/base/status.h" +#include "mongo/bson/mutable/element.h" +#include "mongo/bson/oid.h" +#include "mongo/db/auth/action_set.h" +#include "mongo/db/auth/privilege_format.h" +#include "mongo/db/auth/resource_pattern.h" +#include "mongo/db/auth/role_graph.h" +#include "mongo/db/auth/user.h" +#include "mongo/db/auth/user_name.h" +#include "mongo/db/auth/user_name_hash.h" +#include "mongo/db/jsobj.h" +#include "mongo/db/namespace_string.h" +#include "mongo/db/server_options.h" +#include "mongo/stdx/condition_variable.h" +#include "mongo/stdx/functional.h" +#include "mongo/stdx/mutex.h" +#include "mongo/stdx/unordered_map.h" + +namespace mongo { +class AuthorizationSession; +class AuthzManagerExternalState; +class OperationContext; +class ServiceContext; +class UserDocumentParser; + +/** + * Contains server/cluster-wide information about Authorization. + */ +class AuthorizationManagerImpl : public AuthorizationManager { +public: + ~AuthorizationManagerImpl() override; + + AuthorizationManagerImpl(); + + struct InstallMockForTestingOrAuthImpl { + explicit InstallMockForTestingOrAuthImpl() = default; + }; + + AuthorizationManagerImpl(std::unique_ptr<AuthzManagerExternalState> externalState, + InstallMockForTestingOrAuthImpl); + + std::unique_ptr<AuthorizationSession> makeAuthorizationSession() override; + + void setShouldValidateAuthSchemaOnStartup(bool validate) override; + + bool shouldValidateAuthSchemaOnStartup() override; + + void setAuthEnabled(bool enabled) override; + + bool isAuthEnabled() const override; + + Status getAuthorizationVersion(OperationContext* opCtx, int* version) override; + + OID getCacheGeneration() override; + + bool hasAnyPrivilegeDocuments(OperationContext* opCtx) override; + + Status getUserDescription(OperationContext* opCtx, + const UserName& userName, + BSONObj* result) override; + + Status getRoleDescription(OperationContext* opCtx, + const RoleName& roleName, + PrivilegeFormat privilegeFormat, + AuthenticationRestrictionsFormat, + BSONObj* result) override; + + Status getRolesDescription(OperationContext* opCtx, + const std::vector<RoleName>& roleName, + PrivilegeFormat privilegeFormat, + AuthenticationRestrictionsFormat, + BSONObj* result) override; + + Status getRoleDescriptionsForDB(OperationContext* opCtx, + const std::string dbname, + PrivilegeFormat privilegeFormat, + AuthenticationRestrictionsFormat, + bool showBuiltinRoles, + std::vector<BSONObj>* result) override; + + Status acquireUser(OperationContext* opCtx, + const UserName& userName, + User** acquiredUser) override; + + void releaseUser(User* user) override; + + void invalidateUserByName(const UserName& user) override; + + void invalidateUsersFromDB(const std::string& dbname) override; + + Status initialize(OperationContext* opCtx) override; + + void invalidateUserCache() override; + + Status _initializeUserFromPrivilegeDocument(User* user, const BSONObj& privDoc) override; + + void logOp(OperationContext* opCtx, + const char* opstr, + const NamespaceString& nss, + const BSONObj& obj, + const BSONObj* patt) override; + +private: + /** + * Type used to guard accesses and updates to the user cache. + */ + class CacheGuard; + friend class AuthorizationManagerImpl::CacheGuard; + + /** + * Invalidates all User objects in the cache and removes them from the cache. + * Should only be called when already holding _cacheMutex. + */ + void _invalidateUserCache_inlock(); + + /** + * Given the objects describing an oplog entry that affects authorization data, invalidates + * the portion of the user cache that is affected by that operation. Should only be called + * with oplog entries that have been pre-verified to actually affect authorization data. + */ + void _invalidateRelevantCacheData(const char* op, + const NamespaceString& ns, + const BSONObj& o, + const BSONObj* o2); + + /** + * Updates _cacheGeneration to a new OID + */ + void _updateCacheGeneration_inlock(); + + /** + * Fetches user information from a v2-schema user document for the named user, + * and stores a pointer to a new user object into *acquiredUser on success. + */ + Status _fetchUserV2(OperationContext* opCtx, + const UserName& userName, + std::unique_ptr<User>* acquiredUser); + + /** + * True if AuthSchema startup checks should be applied in this AuthorizationManager. + * + * Defaults to true. Changes to its value are not synchronized, so it should only be set + * at initalization-time. + */ + bool _startupAuthSchemaValidation; + + /** + * True if access control enforcement is enabled in this AuthorizationManager. + * + * Defaults to false. Changes to its value are not synchronized, so it should only be set + * at initalization-time. + */ + bool _authEnabled; + + /** + * A cache of whether there are any users set up for the cluster. + */ + bool _privilegeDocsExist; + + // Protects _privilegeDocsExist + mutable stdx::mutex _privilegeDocsExistMutex; + + std::unique_ptr<AuthzManagerExternalState> _externalState; + + /** + * Cached value of the authorization schema version. + * + * May be set by acquireUser() and getAuthorizationVersion(). Invalidated by + * invalidateUserCache(). + * + * Reads and writes guarded by CacheGuard. + */ + int _version; + + /** + * Caches User objects with information about user privileges, to avoid the need to + * go to disk to read user privilege documents whenever possible. Every User object + * has a reference count - the AuthorizationManager must not delete a User object in the + * cache unless its reference count is zero. + */ + stdx::unordered_map<UserName, User*> _userCache; + + /** + * Current generation of cached data. Updated every time part of the cache gets + * invalidated. Protected by CacheGuard. + */ + OID _cacheGeneration; + + /** + * True if there is an update to the _userCache in progress, and that update is currently in + * the "fetch phase", during which it does not hold the _cacheMutex. + * + * Manipulated via CacheGuard. + */ + bool _isFetchPhaseBusy; + + /** + * Protects _userCache, _cacheGeneration, _version and _isFetchPhaseBusy. Manipulated + * via CacheGuard. + */ + stdx::mutex _cacheMutex; + + /** + * Condition used to signal that it is OK for another CacheGuard to enter a fetch phase. + * Manipulated via CacheGuard. + */ + stdx::condition_variable _fetchPhaseIsReady; +}; +} // namespace mongo diff --git a/src/mongo/db/auth/authorization_manager_mock_init.cpp b/src/mongo/db/auth/authorization_manager_mock_init.cpp deleted file mode 100644 index fd19cef1160..00000000000 --- a/src/mongo/db/auth/authorization_manager_mock_init.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2016 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_manager.h" -#include "mongo/db/auth/authz_manager_external_state.h" -#include "mongo/db/auth/authz_manager_external_state_mock.h" -#include "mongo/stdx/memory.h" - - -// This file provides mock initialization for tests which depend upon, -// but do not use, the auth subsystem. -// TODO: Remove this file once all unused inclusion of auth has been removed. - -namespace mongo { -namespace { - -std::unique_ptr<AuthzManagerExternalState> createAuthzManagerExternalStateMock() { - return stdx::make_unique<AuthzManagerExternalStateMock>(); -} - -MONGO_INITIALIZER(CreateAuthorizationExternalStateFactory)(InitializerContext* context) { - AuthzManagerExternalState::create = &createAuthzManagerExternalStateMock; - return Status::OK(); -} - -} // namespace -} // namespace mongo diff --git a/src/mongo/db/auth/authorization_manager_test.cpp b/src/mongo/db/auth/authorization_manager_test.cpp index 90cd5a6b243..c382e0e8e00 100644 --- a/src/mongo/db/auth/authorization_manager_test.cpp +++ b/src/mongo/db/auth/authorization_manager_test.cpp @@ -38,6 +38,7 @@ #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_manager_impl.h" #include "mongo/db/auth/authorization_session.h" #include "mongo/db/auth/authz_manager_external_state_mock.h" #include "mongo/db/auth/authz_session_external_state_mock.h" @@ -174,7 +175,9 @@ public: void setUp() override { auto localExternalState = stdx::make_unique<AuthzManagerExternalStateMock>(); externalState = localExternalState.get(); - authzManager = stdx::make_unique<AuthorizationManager>(std::move(localExternalState)); + authzManager = stdx::make_unique<AuthorizationManagerImpl>( + std::move(localExternalState), + AuthorizationManagerImpl::InstallMockForTestingOrAuthImpl{}); externalState->setAuthorizationManager(authzManager.get()); authzManager->setAuthEnabled(true); @@ -369,7 +372,9 @@ public: stdx::make_unique<AuthzManagerExternalStateMockWithExplicitUserPrivileges>(); externalState = localExternalState.get(); externalState->setAuthzVersion(AuthorizationManager::schemaVersion26Final); - authzManager = stdx::make_unique<AuthorizationManager>(std::move(localExternalState)); + authzManager = stdx::make_unique<AuthorizationManagerImpl>( + std::move(localExternalState), + AuthorizationManagerImpl::InstallMockForTestingOrAuthImpl{}); externalState->setAuthorizationManager(authzManager.get()); authzManager->setAuthEnabled(true); } diff --git a/src/mongo/db/auth/authorization_session.cpp b/src/mongo/db/auth/authorization_session.cpp index 8c71e6bdcea..6965c5c9809 100644 --- a/src/mongo/db/auth/authorization_session.cpp +++ b/src/mongo/db/auth/authorization_session.cpp @@ -57,1019 +57,14 @@ namespace mongo { -namespace dps = ::mongo::dotted_path_support; -using std::vector; - -namespace { -const std::string ADMIN_DBNAME = "admin"; - -// 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(); - } - - // This check performs some validation but it is not exhaustive and may allow for an invalid - // pipeline specification. In this case the authorization check will succeed but the pipeline - // will fail to parse later in Command::run(). - return authzSession->checkAuthForAggregate( - viewOnNs, - BSON("aggregate" << viewOnNs.coll() << "pipeline" << viewPipeline << "cursor" << BSONObj()), - isMongos); -} - -/** Deleter for User*. - * Will release a User* back to its owning AuthorizationManager on destruction. - * If a borrowing UserSet and the iterator it uses to store the User* is provided, this - * deleter will release the User* from the set if the iterator still points to the deleting User*. - */ -class UserReleaser { -public: - explicit UserReleaser(AuthorizationManager* owner) : _owner(owner), _borrower(nullptr) {} - UserReleaser(AuthorizationManager* owner, UserSet* borrower, UserSet::iterator borrowerIt) - : _owner(owner), _borrower(borrower), _it(borrowerIt) {} - - void operator()(User* user) { - // Remove the user from the borrower if it hasn't already been swapped out. - if (_borrower && *_it == user) { - fassert(40546, _borrower->removeAt(_it) == user); - } - _owner->releaseUser(user); - } - -protected: - AuthorizationManager* _owner; - UserSet* _borrower; - UserSet::iterator _it; -}; -/** Holder for User*s. If this Holder falls out of scope while holding a User*, it will release - * the User* from its AuthorizationManager, and extract it from a UserSet if the set still contains - * it. Use this object to guard User*s which will need to be destroyed in the event of an exception. - */ -using UserHolder = std::unique_ptr<User, UserReleaser>; - -} // namespace - -AuthorizationSession::ScopedImpersonate::ScopedImpersonate(AuthorizationSession* authSession, - std::vector<UserName>* users, - std::vector<RoleName>* roles) - : _authSession(*authSession), _users(*users), _roles(*roles) { - swap(); -} - -AuthorizationSession::ScopedImpersonate::~ScopedImpersonate() { - swap(); -} +AuthorizationSession::~AuthorizationSession() = default; void AuthorizationSession::ScopedImpersonate::swap() { + auto impersonations = _authSession._getImpersonations(); using std::swap; - swap(_authSession._impersonatedUserNames, _users); - swap(_authSession._impersonatedRoleNames, _roles); -} - -AuthorizationSession::AuthorizationSession(std::unique_ptr<AuthzSessionExternalState> externalState) - : _externalState(std::move(externalState)), _impersonationFlag(false) {} - -AuthorizationSession::~AuthorizationSession() { - for (UserSet::iterator it = _authenticatedUsers.begin(); it != _authenticatedUsers.end(); - ++it) { - getAuthorizationManager().releaseUser(*it); - } -} - -AuthorizationManager& AuthorizationSession::getAuthorizationManager() { - return _externalState->getAuthorizationManager(); -} - -void AuthorizationSession::startRequest(OperationContext* opCtx) { - _externalState->startRequest(opCtx); - _refreshUserInfoAsNeeded(opCtx); -} - -Status AuthorizationSession::addAndAuthorizeUser(OperationContext* opCtx, - const UserName& userName) { - User* user; - AuthorizationManager* authzManager = AuthorizationManager::get(opCtx->getServiceContext()); - Status status = authzManager->acquireUser(opCtx, userName, &user); - if (!status.isOK()) { - return status; - } - - UserHolder userHolder(user, UserReleaser(authzManager)); - - const auto& restrictionSet = userHolder->getRestrictions(); - if (opCtx->getClient() == nullptr) { - return Status(ErrorCodes::AuthenticationFailed, - "Unable to evaluate restrictions, OperationContext has no Client"); - } - - Status restrictionStatus = - restrictionSet.validate(RestrictionEnvironment::get(*opCtx->getClient())); - if (!restrictionStatus.isOK()) { - log() << "Failed to acquire user '" << userName - << "' because of unmet authentication restrictions: " << restrictionStatus.reason(); - return AuthorizationManager::authenticationFailedStatus; - } - - // Calling add() on the UserSet may return a user that was replaced because it was from the - // same database. - userHolder.reset(_authenticatedUsers.add(userHolder.release())); - - // If there are any users and roles in the impersonation data, clear it out. - clearImpersonatedUserData(); - - _buildAuthenticatedRolesVector(); - return Status::OK(); -} - -User* AuthorizationSession::lookupUser(const UserName& name) { - return _authenticatedUsers.lookup(name); -} - -User* AuthorizationSession::getSingleUser() { - UserName userName; - - auto userNameItr = getAuthenticatedUserNames(); - if (userNameItr.more()) { - userName = userNameItr.next(); - if (userNameItr.more()) { - uasserted(ErrorCodes::Unauthorized, "too many users are authenticated"); - } - } else { - uasserted(ErrorCodes::Unauthorized, "there are no users authenticated"); - } - - return lookupUser(userName); -} - -void AuthorizationSession::logoutDatabase(const std::string& dbname) { - User* removedUser = _authenticatedUsers.removeByDBName(dbname); - if (removedUser) { - getAuthorizationManager().releaseUser(removedUser); - } - clearImpersonatedUserData(); - _buildAuthenticatedRolesVector(); -} - -bool AuthorizationSession::isAuthenticated() { - return _authenticatedUsers.begin() != _authenticatedUsers.end(); -} - -UserNameIterator AuthorizationSession::getAuthenticatedUserNames() { - return _authenticatedUsers.getNames(); -} - -RoleNameIterator AuthorizationSession::getAuthenticatedRoleNames() { - return makeRoleNameIterator(_authenticatedRoleNames.begin(), _authenticatedRoleNames.end()); -} - -std::string AuthorizationSession::getAuthenticatedUserNamesToken() { - std::string ret; - for (UserNameIterator nameIter = getAuthenticatedUserNames(); nameIter.more(); - nameIter.next()) { - ret += '\0'; // Using a NUL byte which isn't valid in usernames to separate them. - ret += nameIter->getFullName(); - } - - return ret; -} - -void AuthorizationSession::grantInternalAuthorization() { - _authenticatedUsers.add(internalSecurity.user); - _buildAuthenticatedRolesVector(); -} - -PrivilegeVector AuthorizationSession::getDefaultPrivileges() { - PrivilegeVector defaultPrivileges; - - // If localhost exception is active (and no users exist), - // return a vector of the minimum privileges required to bootstrap - // a system and add the first user. - if (_externalState->shouldAllowLocalhost()) { - ResourcePattern adminDBResource = ResourcePattern::forDatabaseName(ADMIN_DBNAME); - ActionSet setupAdminUserActionSet; - setupAdminUserActionSet.addAction(ActionType::createUser); - setupAdminUserActionSet.addAction(ActionType::grantRole); - Privilege setupAdminUserPrivilege = Privilege(adminDBResource, setupAdminUserActionSet); - - ResourcePattern externalDBResource = ResourcePattern::forDatabaseName("$external"); - Privilege setupExternalUserPrivilege = - Privilege(externalDBResource, ActionType::createUser); - - ActionSet setupServerConfigActionSet; - - // If this server is an arbiter, add specific privileges meant to circumvent - // the behavior of an arbiter in an authenticated replset. See SERVER-5479. - if (_externalState->serverIsArbiter()) { - setupServerConfigActionSet.addAction(ActionType::getCmdLineOpts); - setupServerConfigActionSet.addAction(ActionType::getParameter); - setupServerConfigActionSet.addAction(ActionType::serverStatus); - setupServerConfigActionSet.addAction(ActionType::shutdown); - } - - setupServerConfigActionSet.addAction(ActionType::addShard); - setupServerConfigActionSet.addAction(ActionType::replSetConfigure); - setupServerConfigActionSet.addAction(ActionType::replSetGetStatus); - Privilege setupServerConfigPrivilege = - Privilege(ResourcePattern::forClusterResource(), setupServerConfigActionSet); - - Privilege::addPrivilegeToPrivilegeVector(&defaultPrivileges, setupAdminUserPrivilege); - Privilege::addPrivilegeToPrivilegeVector(&defaultPrivileges, setupExternalUserPrivilege); - Privilege::addPrivilegeToPrivilegeVector(&defaultPrivileges, setupServerConfigPrivilege); - return defaultPrivileges; - } - - return defaultPrivileges; -} - -Status AuthorizationSession::checkAuthForAggregate(const NamespaceString& nss, - const BSONObj& cmdObj, - bool isMongos) { - if (!nss.isValid()) { - return Status(ErrorCodes::InvalidNamespace, - mongoutils::str::stream() << "Invalid input namespace, " << nss.ns()); - } - - // If this connection does not need to be authenticated (for instance, if auth is disabled), - // return Status::OK() immediately. - if (_externalState->shouldIgnoreAuthChecks()) { - return Status::OK(); - } - - // We require at least one authenticated user when running aggregate with auth enabled. - if (!isAuthenticated()) { - return Status(ErrorCodes::Unauthorized, "unauthorized"); - } - - auto statusWithAggRequest = AggregationRequest::parseFromBSON(nss, cmdObj); - if (!statusWithAggRequest.isOK()) { - return statusWithAggRequest.getStatus(); - } - AggregationRequest aggRequest = std::move(statusWithAggRequest.getValue()); - - const auto& pipeline = aggRequest.getPipeline(); - - // If the aggregation pipeline is empty, confirm the user is authorized for find on 'nss'. - if (pipeline.empty()) { - if (!isAuthorizedForPrivilege( - Privilege(ResourcePattern::forExactNamespace(nss), ActionType::find))) { - return Status(ErrorCodes::Unauthorized, "unauthorized"); - } - - return Status::OK(); - } - - // Confirm the user is authorized for the pipeline's initial document source. We confirm a user - // is authorized incrementally rather than once for the entire pipeline. This will prevent a - // malicious user, who doesn't have access to the initial document source, from consuming the - // resources needed to parse a potentially large pipeline. - auto liteParsedFirstDocumentSource = LiteParsedDocumentSource::parse(aggRequest, pipeline[0]); - if (!liteParsedFirstDocumentSource->isInitialSource() && - !isAuthorizedForPrivilege( - Privilege(ResourcePattern::forExactNamespace(nss), ActionType::find))) { - return Status(ErrorCodes::Unauthorized, "unauthorized"); - } - - // We have done the work to lite parse the first stage. Given that, we check required privileges - // for it using 'liteParsedFirstDocumentSource' regardless of whether is an initial source or - // not. - if (!isAuthorizedForPrivileges(liteParsedFirstDocumentSource->requiredPrivileges(isMongos))) { - return Status(ErrorCodes::Unauthorized, "unauthorized"); - } - - // Confirm privileges for the remainder of the pipepline. Start with the second stage as we have - // already authorized the first. - auto pipelineIter = pipeline.begin() + 1; - - for (; pipelineIter != pipeline.end(); ++pipelineIter) { - auto liteParsedDocSource = LiteParsedDocumentSource::parse(aggRequest, *pipelineIter); - if (!isAuthorizedForPrivileges(liteParsedDocSource->requiredPrivileges(isMongos))) { - return Status(ErrorCodes::Unauthorized, "unauthorized"); - } - } - - return Status::OK(); -} - -Status AuthorizationSession::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 AuthorizationSession::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 AuthorizationSession::checkAuthForInsert(OperationContext* opCtx, - const NamespaceString& ns, - const BSONObj& document) { - if (ns.coll() == "system.indexes"_sd) { - BSONElement nsElement = document["ns"]; - if (nsElement.type() != String) { - return Status(nsElement.type() == BSONType::EOO ? ErrorCodes::NoSuchKey - : ErrorCodes::TypeMismatch, - "Cannot authorize inserting into " - "system.indexes documents without a string-typed \"ns\" field."); - } - NamespaceString indexNS(nsElement.valueStringData()); - if (!isAuthorizedForActionsOnNamespace(indexNS, ActionType::createIndex)) { - return Status(ErrorCodes::Unauthorized, - str::stream() << "not authorized to create index on " << indexNS.ns()); - } - } else { - ActionSet required{ActionType::insert}; - if (documentValidationDisabled(opCtx)) { - 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 AuthorizationSession::checkAuthForUpdate(OperationContext* opCtx, - const NamespaceString& ns, - const BSONObj& query, - const BSONObj& update, - bool upsert) { - ActionSet required{ActionType::update}; - StringData operationType = "update"_sd; - - if (upsert) { - required.addAction(ActionType::insert); - operationType = "upsert"_sd; - } - - if (documentValidationDisabled(opCtx)) { - 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 AuthorizationSession::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 AuthorizationSession::checkAuthForKillCursors(const NamespaceString& ns, - UserNameIterator cursorOwner) { - if (isAuthorizedForActionsOnResource(ResourcePattern::forClusterResource(), - ActionType::killAnyCursor)) { - return Status::OK(); - } - - if (ns.isListCollectionsCursorNS()) { - // listCollections: Target the database being enumerated. - if (isAuthorizedForActionsOnResource(ResourcePattern::forDatabaseName(ns.db()), - ActionType::killAnyCursor)) { - return Status::OK(); - } - const bool canKill = - isAuthorizedForActionsOnResource(ResourcePattern::forDatabaseName(ns.db()), - ActionType::killCursors) || - isAuthorizedToListCollections(ns.db()); - if (canKill && isCoauthorizedWith(cursorOwner)) { - return Status::OK(); - } - return Status(ErrorCodes::Unauthorized, - str::stream() << "not authorized to kill listCollections cursor on " - << ns.ns()); - } else if (ns.isListIndexesCursorNS()) { - - // listIndexes: Target the underlying collection. - NamespaceString targetNS = ns.getTargetNSForListIndexes(); - if (isAuthorizedForActionsOnNamespace(targetNS, ActionType::killAnyCursor)) { - return Status::OK(); - } - const bool canKill = isAuthorizedForActionsOnNamespace(targetNS, ActionType::killCursors) || - isAuthorizedForActionsOnNamespace(targetNS, ActionType::listIndexes); - if (canKill && isCoauthorizedWith(cursorOwner)) { - return Status::OK(); - } - - return Status(ErrorCodes::Unauthorized, - str::stream() << "not authorized to kill listIndexes cursor on " << ns.ns()); - } else { - - // Otherwise: Target the collection as named. - if (isAuthorizedForActionsOnNamespace(ns, ActionType::killAnyCursor)) { - return Status::OK(); - } - const bool canKill = isAuthorizedForActionsOnNamespace(ns, ActionType::killCursors) || - isAuthorizedForActionsOnNamespace(ns, ActionType::find); - if (canKill && isCoauthorizedWith(cursorOwner)) { - return Status::OK(); - } - return Status(ErrorCodes::Unauthorized, - str::stream() << "not authorized to kill cursor on " << ns.ns()); - } -} - -Status AuthorizationSession::checkAuthForCreate(const NamespaceString& ns, - const BSONObj& cmdObj, - bool isMongos) { - if (cmdObj["capped"].trueValue() && - !isAuthorizedForActionsOnNamespace(ns, ActionType::convertToCapped)) { - return Status(ErrorCodes::Unauthorized, "unauthorized"); - } - - const bool hasCreateCollectionAction = - isAuthorizedForActionsOnNamespace(ns, ActionType::createCollection); - - // If attempting to create a view, check for additional required privileges. - if (cmdObj["viewOn"]) { - // You need the createCollection action on this namespace; the insert action is not - // sufficient. - if (!hasCreateCollectionAction) { - return Status(ErrorCodes::Unauthorized, "unauthorized"); - } - - // Parse the viewOn namespace and the pipeline. If no pipeline was specified, use the empty - // pipeline. - NamespaceString viewOnNs(ns.db(), cmdObj["viewOn"].checkAndGetStringData()); - auto pipeline = - cmdObj.hasField("pipeline") ? BSONArray(cmdObj["pipeline"].Obj()) : BSONArray(); - return checkAuthForCreateOrModifyView(this, ns, viewOnNs, pipeline, isMongos); - } - - // To create a regular collection, ActionType::createCollection or ActionType::insert are - // both acceptable. - if (hasCreateCollectionAction || isAuthorizedForActionsOnNamespace(ns, ActionType::insert)) { - return Status::OK(); - } - - return Status(ErrorCodes::Unauthorized, "unauthorized"); -} - -Status AuthorizationSession::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 AuthorizationSession::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 AuthorizationSession::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 AuthorizationSession::isAuthorizedToParseNamespaceElement(const BSONElement& element) { - const bool isUUID = element.type() == BinData && element.binDataType() == BinDataType::newUUID; - - uassert(ErrorCodes::InvalidNamespace, - "Failed to parse namespace element", - element.type() == String || isUUID); - - if (isUUID) { - return isAuthorizedForActionsOnResource(ResourcePattern::forClusterResource(), - ActionType::useUUID); - } - - return true; -} - -bool AuthorizationSession::isAuthorizedToCreateRole( - const struct auth::CreateOrUpdateRoleArgs& args) { - // A user is allowed to create a role under either of two conditions. - - // The user may create a role if the authorization system says they are allowed to. - if (isAuthorizedForActionsOnResource(ResourcePattern::forDatabaseName(args.roleName.getDB()), - ActionType::createRole)) { - return true; - } - - // The user may create a role if the localhost exception is enabled, and they already own the - // role. This implies they have obtained the role through an external authorization mechanism. - if (_externalState->shouldAllowLocalhost()) { - for (const User* const user : _authenticatedUsers) { - if (user->hasRole(args.roleName)) { - return true; - } - } - log() << "Not authorized to create the first role in the system '" << args.roleName - << "' using the localhost exception. The user needs to acquire the role through " - "external authentication first."; - } - - return false; -} - -bool AuthorizationSession::isAuthorizedToGrantRole(const RoleName& role) { - return isAuthorizedForActionsOnResource(ResourcePattern::forDatabaseName(role.getDB()), - ActionType::grantRole); -} - -bool AuthorizationSession::isAuthorizedToRevokeRole(const RoleName& role) { - return isAuthorizedForActionsOnResource(ResourcePattern::forDatabaseName(role.getDB()), - ActionType::revokeRole); -} - -bool AuthorizationSession::isAuthorizedForPrivilege(const Privilege& privilege) { - if (_externalState->shouldIgnoreAuthChecks()) - return true; - - return _isAuthorizedForPrivilege(privilege); -} - -bool AuthorizationSession::isAuthorizedForPrivileges(const vector<Privilege>& privileges) { - if (_externalState->shouldIgnoreAuthChecks()) - return true; - - for (size_t i = 0; i < privileges.size(); ++i) { - if (!_isAuthorizedForPrivilege(privileges[i])) - return false; - } - - return true; -} - -bool AuthorizationSession::isAuthorizedForActionsOnResource(const ResourcePattern& resource, - ActionType action) { - return isAuthorizedForPrivilege(Privilege(resource, action)); -} - -bool AuthorizationSession::isAuthorizedForActionsOnResource(const ResourcePattern& resource, - const ActionSet& actions) { - return isAuthorizedForPrivilege(Privilege(resource, actions)); -} - -bool AuthorizationSession::isAuthorizedForActionsOnNamespace(const NamespaceString& ns, - ActionType action) { - return isAuthorizedForPrivilege(Privilege(ResourcePattern::forExactNamespace(ns), action)); -} - -bool AuthorizationSession::isAuthorizedForActionsOnNamespace(const NamespaceString& ns, - const ActionSet& actions) { - return isAuthorizedForPrivilege(Privilege(ResourcePattern::forExactNamespace(ns), actions)); -} - -static const int resourceSearchListCapacity = 5; -/** - * Builds from "target" an exhaustive list of all ResourcePatterns that match "target". - * - * Stores the resulting list into resourceSearchList, and returns the length. - * - * The seach lists are as follows, depending on the type of "target": - * - * target is ResourcePattern::forAnyResource(): - * searchList = { ResourcePattern::forAnyResource(), ResourcePattern::forAnyResource() } - * target is the ResourcePattern::forClusterResource(): - * searchList = { ResourcePattern::forAnyResource(), ResourcePattern::forClusterResource() } - * target is a database, db: - * searchList = { ResourcePattern::forAnyResource(), - * ResourcePattern::forAnyNormalResource(), - * db } - * target is a non-system collection, db.coll: - * searchList = { ResourcePattern::forAnyResource(), - * ResourcePattern::forAnyNormalResource(), - * db, - * coll, - * db.coll } - * target is a system collection, db.system.coll: - * searchList = { ResourcePattern::forAnyResource(), - * system.coll, - * db.system.coll } - */ -static int buildResourceSearchList(const ResourcePattern& target, - ResourcePattern resourceSearchList[resourceSearchListCapacity]) { - int size = 0; - resourceSearchList[size++] = ResourcePattern::forAnyResource(); - if (target.isExactNamespacePattern()) { - if (!target.ns().isSystem()) { - // Some databases should not be matchable with ResourcePattern::forAnyNormalResource. - // 'local' and 'config' are used to store special system collections, which user level - // administrators should not be able to manipulate. - if (target.ns().db() != "local" && target.ns().db() != "config") { - resourceSearchList[size++] = ResourcePattern::forAnyNormalResource(); - } - resourceSearchList[size++] = ResourcePattern::forDatabaseName(target.ns().db()); - } - resourceSearchList[size++] = ResourcePattern::forCollectionName(target.ns().coll()); - } else if (target.isDatabasePattern()) { - resourceSearchList[size++] = ResourcePattern::forAnyNormalResource(); - } - resourceSearchList[size++] = target; - dassert(size <= resourceSearchListCapacity); - return size; -} - -bool AuthorizationSession::isAuthorizedToChangeAsUser(const UserName& userName, - ActionType actionType) { - User* user = lookupUser(userName); - if (!user) { - return false; - } - ResourcePattern resourceSearchList[resourceSearchListCapacity]; - const int resourceSearchListLength = buildResourceSearchList( - ResourcePattern::forDatabaseName(userName.getDB()), resourceSearchList); - - ActionSet actions; - for (int i = 0; i < resourceSearchListLength; ++i) { - actions.addAllActionsFromSet(user->getActionsForResource(resourceSearchList[i])); - } - return actions.contains(actionType); -} - -bool AuthorizationSession::isAuthorizedToChangeOwnPasswordAsUser(const UserName& userName) { - return AuthorizationSession::isAuthorizedToChangeAsUser(userName, - ActionType::changeOwnPassword); -} - -bool AuthorizationSession::isAuthorizedToChangeOwnCustomDataAsUser(const UserName& userName) { - return AuthorizationSession::isAuthorizedToChangeAsUser(userName, - ActionType::changeOwnCustomData); -} - -bool AuthorizationSession::isAuthorizedToListCollections(StringData dbname) { - // Check for the listCollections ActionType on the database or find on system.namespaces for - // pre 3.0 systems. - - return AuthorizationSession::isAuthorizedForActionsOnResource( - ResourcePattern::forDatabaseName(dbname), ActionType::listCollections) || - AuthorizationSession::isAuthorizedForActionsOnResource( - ResourcePattern::forExactNamespace(NamespaceString(dbname, "system.namespaces")), - ActionType::find); -} - -bool AuthorizationSession::isAuthenticatedAsUserWithRole(const RoleName& roleName) { - for (UserSet::iterator it = _authenticatedUsers.begin(); it != _authenticatedUsers.end(); - ++it) { - if ((*it)->hasRole(roleName)) { - return true; - } - } - return false; -} - -void AuthorizationSession::_refreshUserInfoAsNeeded(OperationContext* opCtx) { - AuthorizationManager& authMan = getAuthorizationManager(); - UserSet::iterator it = _authenticatedUsers.begin(); - while (it != _authenticatedUsers.end()) { - User* user = *it; - - if (!user->isValid()) { - // Make a good faith effort to acquire an up-to-date user object, since the one - // we've cached is marked "out-of-date." - UserName name = user->getName(); - User* updatedUser; - - Status status = authMan.acquireUser(opCtx, name, &updatedUser); - switch (status.code()) { - case ErrorCodes::OK: { - - // Verify the updated user object's authentication restrictions. - UserHolder userHolder(user, UserReleaser(&authMan, &_authenticatedUsers, it)); - UserHolder updatedUserHolder(updatedUser, UserReleaser(&authMan)); - try { - const auto& restrictionSet = - updatedUserHolder->getRestrictions(); // Owned by updatedUser - invariant(opCtx->getClient()); - Status restrictionStatus = restrictionSet.validate( - RestrictionEnvironment::get(*opCtx->getClient())); - if (!restrictionStatus.isOK()) { - log() << "Removed user " << name - << " with unmet authentication restrictions from session cache of" - << " user information. Restriction failed because: " - << restrictionStatus.reason(); - // If we remove from the UserSet, we cannot increment the iterator. - continue; - } - } catch (...) { - log() << "Evaluating authentication restrictions for " << name - << " resulted in an unknown exception. Removing user from the" - << " session cache."; - continue; - } - - // Success! Replace the old User object with the updated one. - fassert(17067, - _authenticatedUsers.replaceAt(it, updatedUserHolder.release()) == - userHolder.get()); - LOG(1) << "Updated session cache of user information for " << name; - break; - } - case ErrorCodes::UserNotFound: { - // User does not exist anymore; remove it from _authenticatedUsers. - fassert(17068, _authenticatedUsers.removeAt(it) == user); - authMan.releaseUser(user); - log() << "Removed deleted user " << name - << " from session cache of user information."; - continue; // No need to advance "it" in this case. - } - case ErrorCodes::UnsupportedFormat: { - // An auth subsystem has explicitly indicated a failure. - fassert(40555, _authenticatedUsers.removeAt(it) == user); - authMan.releaseUser(user); - log() << "Removed user " << name - << " from session cache of user information because of refresh failure:" - << " '" << status << "'."; - continue; // No need to advance "it" in this case. - } - default: - // Unrecognized error; assume that it's transient, and continue working with the - // out-of-date privilege data. - warning() << "Could not fetch updated user privilege information for " << name - << "; continuing to use old information. Reason is " - << redact(status); - break; - } - } - ++it; - } - _buildAuthenticatedRolesVector(); -} - -void AuthorizationSession::_buildAuthenticatedRolesVector() { - _authenticatedRoleNames.clear(); - for (UserSet::iterator it = _authenticatedUsers.begin(); it != _authenticatedUsers.end(); - ++it) { - RoleNameIterator roles = (*it)->getIndirectRoles(); - while (roles.more()) { - RoleName roleName = roles.next(); - _authenticatedRoleNames.push_back(RoleName(roleName.getRole(), roleName.getDB())); - } - } -} - -bool AuthorizationSession::_isAuthorizedForPrivilege(const Privilege& privilege) { - const ResourcePattern& target(privilege.getResourcePattern()); - - ResourcePattern resourceSearchList[resourceSearchListCapacity]; - const int resourceSearchListLength = buildResourceSearchList(target, resourceSearchList); - - ActionSet unmetRequirements = privilege.getActions(); - - PrivilegeVector defaultPrivileges = getDefaultPrivileges(); - for (PrivilegeVector::iterator it = defaultPrivileges.begin(); it != defaultPrivileges.end(); - ++it) { - for (int i = 0; i < resourceSearchListLength; ++i) { - if (!(it->getResourcePattern() == resourceSearchList[i])) - continue; - - ActionSet userActions = it->getActions(); - unmetRequirements.removeAllActionsFromSet(userActions); - - if (unmetRequirements.empty()) - return true; - } - } - - for (UserSet::iterator it = _authenticatedUsers.begin(); it != _authenticatedUsers.end(); - ++it) { - User* user = *it; - for (int i = 0; i < resourceSearchListLength; ++i) { - ActionSet userActions = user->getActionsForResource(resourceSearchList[i]); - unmetRequirements.removeAllActionsFromSet(userActions); - - if (unmetRequirements.empty()) - return true; - } - } - - return false; -} - -void AuthorizationSession::setImpersonatedUserData(std::vector<UserName> usernames, - std::vector<RoleName> roles) { - _impersonatedUserNames = usernames; - _impersonatedRoleNames = roles; - _impersonationFlag = true; -} - -bool AuthorizationSession::isCoauthorizedWithClient(Client* opClient) { - auto getUserNames = [](AuthorizationSession* authSession) { - if (authSession->isImpersonating()) { - return authSession->getImpersonatedUserNames(); - } else { - return authSession->getAuthenticatedUserNames(); - } - }; - - UserNameIterator it = getUserNames(this); - while (it.more()) { - UserNameIterator opIt = getUserNames(AuthorizationSession::get(opClient)); - while (opIt.more()) { - if (it.get() == opIt.get()) { - return true; - } - opIt.next(); - } - it.next(); - } - - return false; -} - -bool AuthorizationSession::isCoauthorizedWith(UserNameIterator userNameIter) { - if (!getAuthorizationManager().isAuthEnabled()) { - return true; - } - if (!userNameIter.more() && !isAuthenticated()) { - return true; - } - - for (; userNameIter.more(); userNameIter.next()) { - for (UserNameIterator thisUserNameIter = getAuthenticatedUserNames(); - thisUserNameIter.more(); - thisUserNameIter.next()) { - if (*userNameIter == *thisUserNameIter) { - return true; - } - } - } - - return false; -} - -UserNameIterator AuthorizationSession::getImpersonatedUserNames() { - return makeUserNameIterator(_impersonatedUserNames.begin(), _impersonatedUserNames.end()); -} - -RoleNameIterator AuthorizationSession::getImpersonatedRoleNames() { - return makeRoleNameIterator(_impersonatedRoleNames.begin(), _impersonatedRoleNames.end()); -} - -bool AuthorizationSession::isUsingLocalhostBypass() { - return getAuthorizationManager().isAuthEnabled() && _externalState->shouldAllowLocalhost(); -} - -// Clear the vectors of impersonated usernames and roles. -void AuthorizationSession::clearImpersonatedUserData() { - _impersonatedUserNames.clear(); - _impersonatedRoleNames.clear(); - _impersonationFlag = false; -} - - -bool AuthorizationSession::isImpersonating() const { - return _impersonationFlag; + swap(*std::get<0>(impersonations), _users); + swap(*std::get<1>(impersonations), _roles); } +MONGO_DEFINE_SHIM(AuthorizationSession::create); } // namespace mongo - -auto mongo::checkCursorSessionPrivilege(OperationContext* const opCtx, - const boost::optional<LogicalSessionId> cursorSessionId) - -> Status { - if (!AuthorizationSession::exists(opCtx->getClient())) { - return Status::OK(); - } - auto* const authSession = AuthorizationSession::get(opCtx->getClient()); - - auto authHasImpersonatePrivilege = [authSession] { - return authSession->isAuthorizedForPrivilege( - Privilege(ResourcePattern::forClusterResource(), ActionType::impersonate)); - }; - - auto authIsOn = [authSession] { - return authSession->getAuthorizationManager().isAuthEnabled(); - }; - - auto sessionIdToStringOrNone = - [](const boost::optional<LogicalSessionId>& sessionId) -> std::string { - if (sessionId) { - return str::stream() << *sessionId; - } - return "none"; - }; - - // If the cursor has a session then one of the following must be true: - // 1: context session id must match cursor session id. - // 2: user must be magic special (__system, or background task, etc). - - // We do not check the user's ID against the cursor's notion of a user ID, since higher level - // auth checks will check that for us anyhow. - if (authIsOn() && // If the authorization is not on, then we permit anybody to do anything. - cursorSessionId != opCtx->getLogicalSessionId() && // If the cursor's session doesn't match - // the Operation Context's session, then - // we should forbid the operation even - // when the cursor has no session. - authSession->isAuthenticated() && // Unless, for some reason a user isn't actually using - // this Operation Context (which implies a background - // job) - !authHasImpersonatePrivilege() // Or if the user has an impersonation privilege, in which - // case, the user gets to sidestep certain checks. - ) { - return Status{ErrorCodes::Unauthorized, - str::stream() << "Cursor session id (" - << sessionIdToStringOrNone(cursorSessionId) - << ") is not the same as the operation context's session id (" - << sessionIdToStringOrNone(opCtx->getLogicalSessionId()) - << ")"}; - } - - return Status::OK(); -} diff --git a/src/mongo/db/auth/authorization_session.h b/src/mongo/db/auth/authorization_session.h index e260d859651..9b561c467d4 100644 --- a/src/mongo/db/auth/authorization_session.h +++ b/src/mongo/db/auth/authorization_session.h @@ -1,5 +1,5 @@ /** -* Copyright (C) 2012 10gen Inc. +* Copyright (C) 2018 10gen 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, @@ -32,7 +32,6 @@ #include <string> #include <vector> -#include "mongo/base/disallow_copying.h" #include "mongo/base/status.h" #include "mongo/db/auth/action_set.h" #include "mongo/db/auth/action_type.h" @@ -48,7 +47,7 @@ namespace mongo { namespace auth { struct CreateOrUpdateRoleArgs; -} +} // namespace auth class Client; /** @@ -66,9 +65,15 @@ class Client; * the lifetime of the operation. */ class AuthorizationSession { - MONGO_DISALLOW_COPYING(AuthorizationSession); + AuthorizationSession(const AuthorizationSession&) = delete; + AuthorizationSession& operator=(const AuthorizationSession&) = delete; public: + static MONGO_DECLARE_SHIM( + (AuthorizationManager * authzManager)->std::unique_ptr<AuthorizationSession>) create; + + AuthorizationSession() = default; + /** * Provides a way to swap out impersonate data for the duration of the ScopedImpersonate's * lifetime. @@ -77,8 +82,14 @@ public: public: ScopedImpersonate(AuthorizationSession* authSession, std::vector<UserName>* users, - std::vector<RoleName>* roles); - ~ScopedImpersonate(); + std::vector<RoleName>* roles) + : _authSession(*authSession), _users(*users), _roles(*roles) { + swap(); + } + + ~ScopedImpersonate() { + this->swap(); + } private: void swap(); @@ -118,245 +129,242 @@ public: static void set(Client* client, std::unique_ptr<AuthorizationSession> session); // Takes ownership of the externalState. - explicit AuthorizationSession(std::unique_ptr<AuthzSessionExternalState> externalState); - ~AuthorizationSession(); + virtual ~AuthorizationSession() = 0; - AuthorizationManager& getAuthorizationManager(); + virtual AuthorizationManager& getAuthorizationManager() = 0; // Should be called at the beginning of every new request. This performs the checks // necessary to determine if localhost connections should be given full access. // TODO: try to eliminate the need for this call. - void startRequest(OperationContext* opCtx); + virtual void startRequest(OperationContext* opCtx) = 0; /** * Adds the User identified by "UserName" to the authorization session, acquiring privileges * for it in the process. */ - Status addAndAuthorizeUser(OperationContext* opCtx, const UserName& userName); + virtual Status addAndAuthorizeUser(OperationContext* opCtx, const UserName& userName) = 0; // Returns the authenticated user with the given name. Returns NULL // if no such user is found. // The user remains in the _authenticatedUsers set for this AuthorizationSession, // and ownership of the user stays with the AuthorizationManager - User* lookupUser(const UserName& name); + virtual User* lookupUser(const UserName& name) = 0; // Returns the single user on this auth session. If no user is authenticated, or if // multiple users are authenticated, this method will throw an exception. - User* getSingleUser(); + virtual User* getSingleUser() = 0; // Is authenticated as at least one user. - bool isAuthenticated(); + virtual bool isAuthenticated() = 0; // Gets an iterator over the names of all authenticated users stored in this manager. - UserNameIterator getAuthenticatedUserNames(); + virtual UserNameIterator getAuthenticatedUserNames() = 0; // Gets an iterator over the roles of all authenticated users stored in this manager. - RoleNameIterator getAuthenticatedRoleNames(); + virtual RoleNameIterator getAuthenticatedRoleNames() = 0; // Returns a std::string representing all logged-in users on the current session. // WARNING: this std::string will contain NUL bytes so don't call c_str()! - std::string getAuthenticatedUserNamesToken(); + virtual std::string getAuthenticatedUserNamesToken() = 0; // Removes any authenticated principals whose authorization credentials came from the given // database, and revokes any privileges that were granted via that principal. - void logoutDatabase(const std::string& dbname); + virtual void logoutDatabase(const std::string& dbname) = 0; // Adds the internalSecurity user to the set of authenticated users. // Used to grant internal threads full access. - void grantInternalAuthorization(); + virtual void grantInternalAuthorization() = 0; // Generates a vector of default privileges that are granted to any user, // regardless of which roles that user does or does not possess. // If localhost exception is active, the permissions include the ability to create // the first user and the ability to run the commands needed to bootstrap the system // into a state where the first user can be created. - PrivilegeVector getDefaultPrivileges(); + virtual PrivilegeVector getDefaultPrivileges() = 0; // Checks if this connection has the privileges necessary to perform a find operation // on the supplied namespace identifier. - Status checkAuthForFind(const NamespaceString& ns, bool hasTerm); + virtual Status checkAuthForFind(const NamespaceString& ns, bool hasTerm) = 0; // Checks if this connection has the privileges necessary to perform a getMore operation on // the identified cursor, supposing that cursor is associated with the supplied namespace // identifier. - Status checkAuthForGetMore(const NamespaceString& ns, long long cursorID, bool hasTerm); + virtual Status checkAuthForGetMore(const NamespaceString& ns, + long long cursorID, + bool hasTerm) = 0; // Checks if this connection has the privileges necessary to perform the given update on the // given namespace. - Status checkAuthForUpdate(OperationContext* opCtx, - const NamespaceString& ns, - const BSONObj& query, - const BSONObj& update, - bool upsert); + virtual Status checkAuthForUpdate(OperationContext* opCtx, + const NamespaceString& ns, + const BSONObj& query, + const BSONObj& update, + bool upsert) = 0; // Checks if this connection has the privileges necessary to insert the given document // to the given namespace. Correctly interprets inserts to system.indexes and performs // the proper auth checks for index building. - Status checkAuthForInsert(OperationContext* opCtx, - const NamespaceString& ns, - const BSONObj& document); + virtual Status checkAuthForInsert(OperationContext* opCtx, + const NamespaceString& ns, + const BSONObj& document) = 0; // Checks if this connection has the privileges necessary to perform a delete on the given // namespace. - Status checkAuthForDelete(OperationContext* opCtx, - const NamespaceString& ns, - const BSONObj& query); + virtual Status checkAuthForDelete(OperationContext* opCtx, + const NamespaceString& ns, + const BSONObj& query) = 0; // Checks if this connection has the privileges necessary to perform a killCursor on // the identified cursor, supposing that cursor is associated with the supplied namespace // identifier. - Status checkAuthForKillCursors(const NamespaceString& cursorNss, UserNameIterator cursorOwner); + virtual Status checkAuthForKillCursors(const NamespaceString& cursorNss, + UserNameIterator cursorOwner) = 0; // Checks if this connection has the privileges necessary to run the aggregation pipeline // specified in 'cmdObj' on the namespace 'ns' either directly on mongoD or via mongoS. - Status checkAuthForAggregate(const NamespaceString& ns, const BSONObj& cmdObj, bool isMongos); + virtual Status checkAuthForAggregate(const NamespaceString& ns, + const BSONObj& cmdObj, + bool isMongos) = 0; // Checks if this connection has the privileges necessary to create 'ns' with the options // supplied in 'cmdObj' either directly on mongoD or via mongoS. - Status checkAuthForCreate(const NamespaceString& ns, const BSONObj& cmdObj, bool isMongos); + virtual Status checkAuthForCreate(const NamespaceString& ns, + const BSONObj& cmdObj, + bool isMongos) = 0; // Checks if this connection has the privileges necessary to modify 'ns' with the options // supplied in 'cmdObj' either directly on mongoD or via mongoS. - Status checkAuthForCollMod(const NamespaceString& ns, const BSONObj& cmdObj, bool isMongos); + virtual Status checkAuthForCollMod(const NamespaceString& ns, + const BSONObj& cmdObj, + bool isMongos) = 0; // Checks if this connection has the privileges necessary to grant the given privilege // to a role. - Status checkAuthorizedToGrantPrivilege(const Privilege& privilege); + virtual Status checkAuthorizedToGrantPrivilege(const Privilege& privilege) = 0; // Checks if this connection has the privileges necessary to revoke the given privilege // from a role. - Status checkAuthorizedToRevokePrivilege(const Privilege& privilege); + virtual Status checkAuthorizedToRevokePrivilege(const Privilege& privilege) = 0; // Checks if this connection is using the localhost bypass - bool isUsingLocalhostBypass(); + virtual bool isUsingLocalhostBypass() = 0; // Checks if this connection has the privileges necessary to parse a namespace from a // given BSONElement. - bool isAuthorizedToParseNamespaceElement(const BSONElement& elem); + virtual bool isAuthorizedToParseNamespaceElement(const BSONElement& elem) = 0; // Checks if this connection has the privileges necessary to create a new role - bool isAuthorizedToCreateRole(const auth::CreateOrUpdateRoleArgs& args); + virtual bool isAuthorizedToCreateRole(const auth::CreateOrUpdateRoleArgs& args) = 0; // Utility function for isAuthorizedForActionsOnResource( // ResourcePattern::forDatabaseName(role.getDB()), ActionType::grantAnyRole) - bool isAuthorizedToGrantRole(const RoleName& role); + virtual bool isAuthorizedToGrantRole(const RoleName& role) = 0; // Utility function for isAuthorizedForActionsOnResource( // ResourcePattern::forDatabaseName(role.getDB()), ActionType::grantAnyRole) - bool isAuthorizedToRevokeRole(const RoleName& role); + virtual bool isAuthorizedToRevokeRole(const RoleName& role) = 0; // Utility function for isAuthorizedToChangeOwnPasswordAsUser and // isAuthorizedToChangeOwnCustomDataAsUser - bool isAuthorizedToChangeAsUser(const UserName& userName, ActionType actionType); + virtual bool isAuthorizedToChangeAsUser(const UserName& userName, ActionType actionType) = 0; // Returns true if the current session is authenticated as the given user and that user // is allowed to change his/her own password - bool isAuthorizedToChangeOwnPasswordAsUser(const UserName& userName); + virtual bool isAuthorizedToChangeOwnPasswordAsUser(const UserName& userName) = 0; // Returns true if the current session is authorized to list the collections in the given // database. - bool isAuthorizedToListCollections(StringData dbname); + virtual bool isAuthorizedToListCollections(StringData dbname) = 0; // Returns true if the current session is authenticated as the given user and that user // is allowed to change his/her own customData. - bool isAuthorizedToChangeOwnCustomDataAsUser(const UserName& userName); + virtual bool isAuthorizedToChangeOwnCustomDataAsUser(const UserName& userName) = 0; // Returns true if any of the authenticated users on this session have the given role. // NOTE: this does not refresh any of the users even if they are marked as invalid. - bool isAuthenticatedAsUserWithRole(const RoleName& roleName); + virtual bool isAuthenticatedAsUserWithRole(const RoleName& roleName) = 0; // Returns true if this session is authorized for the given Privilege. // // Contains all the authorization logic including handling things like the localhost // exception. - bool isAuthorizedForPrivilege(const Privilege& privilege); + virtual bool isAuthorizedForPrivilege(const Privilege& privilege) = 0; // Like isAuthorizedForPrivilege, above, except returns true if the session is authorized // for all of the listed privileges. - bool isAuthorizedForPrivileges(const std::vector<Privilege>& privileges); + virtual bool isAuthorizedForPrivileges(const std::vector<Privilege>& privileges) = 0; // Utility function for isAuthorizedForPrivilege(Privilege(resource, action)). - bool isAuthorizedForActionsOnResource(const ResourcePattern& resource, ActionType action); + virtual bool isAuthorizedForActionsOnResource(const ResourcePattern& resource, + ActionType action) = 0; // Utility function for isAuthorizedForPrivilege(Privilege(resource, actions)). - bool isAuthorizedForActionsOnResource(const ResourcePattern& resource, - const ActionSet& actions); + virtual bool isAuthorizedForActionsOnResource(const ResourcePattern& resource, + const ActionSet& actions) = 0; // Utility function for // isAuthorizedForActionsOnResource(ResourcePattern::forExactNamespace(ns), action). - bool isAuthorizedForActionsOnNamespace(const NamespaceString& ns, ActionType action); + virtual bool isAuthorizedForActionsOnNamespace(const NamespaceString& ns, + ActionType action) = 0; // Utility function for // isAuthorizedForActionsOnResource(ResourcePattern::forExactNamespace(ns), actions). - bool isAuthorizedForActionsOnNamespace(const NamespaceString& ns, const ActionSet& actions); + virtual bool isAuthorizedForActionsOnNamespace(const NamespaceString& ns, + const ActionSet& actions) = 0; // Replaces the data for users that a system user is impersonating with new data. // The auditing system adds these users and their roles to each audit record in the log. - void setImpersonatedUserData(std::vector<UserName> usernames, std::vector<RoleName> roles); + virtual void setImpersonatedUserData(std::vector<UserName> usernames, + std::vector<RoleName> roles) = 0; // Gets an iterator over the names of all users that the system user is impersonating. - UserNameIterator getImpersonatedUserNames(); + virtual UserNameIterator getImpersonatedUserNames() = 0; // Gets an iterator over the roles of all users that the system user is impersonating. - RoleNameIterator getImpersonatedRoleNames(); + virtual RoleNameIterator getImpersonatedRoleNames() = 0; // Clears the data for impersonated users. - void clearImpersonatedUserData(); + virtual void clearImpersonatedUserData() = 0; // Returns true if the session and 'opClient's AuthorizationSession share an // authenticated user. If either object has impersonated users, // those users will be considered as 'authenticated' for the purpose of this check. // // The existence of 'opClient' must be guaranteed through locks taken by the caller. - bool isCoauthorizedWithClient(Client* opClient); + virtual bool isCoauthorizedWithClient(Client* opClient) = 0; // Returns true if the session and 'userNameIter' share an authenticated user, or if both have // no authenticated users. Impersonated users are not considered as 'authenticated' for the // purpose of this check. This always returns true if auth is not enabled. - bool isCoauthorizedWith(UserNameIterator userNameIter); + virtual bool isCoauthorizedWith(UserNameIterator userNameIter) = 0; // Tells whether impersonation is active or not. This state is set when // setImpersonatedUserData is called and cleared when clearImpersonatedUserData is // called. - bool isImpersonating() const; - -protected: - // Builds a vector of all roles held by users who are authenticated on this connection. The - // vector is stored in _authenticatedRoleNames. This function is called when users are - // logged in or logged out, as well as when the user cache is determined to be out of date. - void _buildAuthenticatedRolesVector(); - - // All Users who have been authenticated on this connection. - UserSet _authenticatedUsers; + virtual bool isImpersonating() const = 0; - // The roles of the authenticated users. This vector is generated when the authenticated - // users set is changed. - std::vector<RoleName> _authenticatedRoleNames; + // Returns a status encoding whether the current session in the specified `opCtx` has privilege + // to access a cursor in the specified `cursorSessionId` parameter. Returns `Status::OK()`, + // when the session is accessible. Returns a `mongo::Status` with information regarding the + // nature of session inaccessibility when the session is not accessible. + virtual Status checkCursorSessionPrivilege( + OperationContext* const opCtx, boost::optional<LogicalSessionId> cursorSessionId) = 0; -private: - // If any users authenticated on this session are marked as invalid this updates them with - // up-to-date information. May require a read lock on the "admin" db to read the user data. - void _refreshUserInfoAsNeeded(OperationContext* opCtx); - - - // Checks if this connection is authorized for the given Privilege, ignoring whether or not - // we should even be doing authorization checks in general. Note: this may acquire a read - // lock on the admin database (to update out-of-date user privilege information). - bool _isAuthorizedForPrivilege(const Privilege& privilege); - - std::unique_ptr<AuthzSessionExternalState> _externalState; - - // A vector of impersonated UserNames and a vector of those users' RoleNames. - // These are used in the auditing system. They are not used for authz checks. - std::vector<UserName> _impersonatedUserNames; - std::vector<RoleName> _impersonatedRoleNames; - bool _impersonationFlag; +protected: + virtual std::tuple<std::vector<UserName>*, std::vector<RoleName>*> _getImpersonations() = 0; }; // Returns a status encoding whether the current session in the specified `opCtx` has privilege to // access a cursor in the specified `cursorSessionId` parameter. Returns `Status::OK()`, when the // session is accessible. Returns a `mongo::Status` with information regarding the nature of // session inaccessibility when the session is not accessible. -Status checkCursorSessionPrivilege(OperationContext* const opCtx, - const boost::optional<LogicalSessionId> cursorSessionId); +inline Status checkCursorSessionPrivilege(OperationContext* const opCtx, + const boost::optional<LogicalSessionId> cursorSessionId) { + if (!AuthorizationSession::exists(opCtx->getClient())) { + return Status::OK(); + } + auto* const authSession = AuthorizationSession::get(opCtx->getClient()); + return authSession->checkCursorSessionPrivilege(opCtx, cursorSessionId); +} + } // namespace mongo diff --git a/src/mongo/db/auth/authorization_session_for_test.h b/src/mongo/db/auth/authorization_session_for_test.h index f57e8669692..6e9f63418de 100644 --- a/src/mongo/db/auth/authorization_session_for_test.h +++ b/src/mongo/db/auth/authorization_session_for_test.h @@ -32,15 +32,16 @@ #include <vector> #include "mongo/db/auth/authorization_session.h" +#include "mongo/db/auth/authorization_session_impl.h" #include "mongo/db/auth/user.h" namespace mongo { -class AuthorizationSessionForTest : public AuthorizationSession { +class AuthorizationSessionForTest : public AuthorizationSessionImpl { MONGO_DISALLOW_COPYING(AuthorizationSessionForTest); public: - using AuthorizationSession::AuthorizationSession; + using AuthorizationSessionImpl::AuthorizationSessionImpl; // A database name used for testing purposes, deliberately named to minimize collisions with // other test users. diff --git a/src/mongo/db/auth/authorization_session_impl.cpp b/src/mongo/db/auth/authorization_session_impl.cpp new file mode 100644 index 00000000000..11966d78a92 --- /dev/null +++ b/src/mongo/db/auth/authorization_session_impl.cpp @@ -0,0 +1,1063 @@ +/** + * Copyright (C) 2018 10gen 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. + */ + +#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kAccessControl + +#include "mongo/platform/basic.h" + +#include "mongo/db/auth/authorization_session_impl.h" + +#include <string> +#include <vector> + +#include "mongo/base/status.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/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/pipeline/aggregation_request.h" +#include "mongo/db/pipeline/lite_parsed_pipeline.h" +#include "mongo/util/assert_util.h" +#include "mongo/util/log.h" +#include "mongo/util/mongoutils/str.h" + +namespace mongo { + +namespace dps = ::mongo::dotted_path_support; +using std::vector; + +MONGO_REGISTER_SHIM(AuthorizationSession::create) +(AuthorizationManager* authzManager)->std::unique_ptr<AuthorizationSession> { + return std::make_unique<AuthorizationSessionImpl>( + AuthzSessionExternalState::create(authzManager), + AuthorizationSessionImpl::InstallMockForTestingOrAuthImpl{}); +} + +namespace { +const std::string ADMIN_DBNAME = "admin"; + +// 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(); + } + + // This check performs some validation but it is not exhaustive and may allow for an invalid + // pipeline specification. In this case the authorization check will succeed but the pipeline + // will fail to parse later in Command::run(). + return authzSession->checkAuthForAggregate( + viewOnNs, + BSON("aggregate" << viewOnNs.coll() << "pipeline" << viewPipeline << "cursor" << BSONObj()), + isMongos); +} + +/** Deleter for User*. + * Will release a User* back to its owning AuthorizationManager on destruction. + * If a borrowing UserSet and the iterator it uses to store the User* is provided, this + * deleter will release the User* from the set if the iterator still points to the deleting User*. + */ +class UserReleaser { +public: + explicit UserReleaser(AuthorizationManager* owner) : _owner(owner), _borrower(nullptr) {} + UserReleaser(AuthorizationManager* owner, UserSet* borrower, UserSet::iterator borrowerIt) + : _owner(owner), _borrower(borrower), _it(borrowerIt) {} + + void operator()(User* user) { + // Remove the user from the borrower if it hasn't already been swapped out. + if (_borrower && *_it == user) { + fassert(40546, _borrower->removeAt(_it) == user); + } + _owner->releaseUser(user); + } + +protected: + AuthorizationManager* _owner; + UserSet* _borrower; + UserSet::iterator _it; +}; +/** Holder for User*s. If this Holder falls out of scope while holding a User*, it will release + * the User* from its AuthorizationManager, and extract it from a UserSet if the set still contains + * it. Use this object to guard User*s which will need to be destroyed in the event of an exception. + */ +using UserHolder = std::unique_ptr<User, UserReleaser>; + +} // namespace + +AuthorizationSessionImpl::AuthorizationSessionImpl( + std::unique_ptr<AuthzSessionExternalState> externalState, InstallMockForTestingOrAuthImpl) + : _externalState(std::move(externalState)), _impersonationFlag(false) {} + +AuthorizationSessionImpl::~AuthorizationSessionImpl() { + for (auto& user : _authenticatedUsers) { + getAuthorizationManager().releaseUser(user); + } +} + +AuthorizationManager& AuthorizationSessionImpl::getAuthorizationManager() { + return _externalState->getAuthorizationManager(); +} + +void AuthorizationSessionImpl::startRequest(OperationContext* opCtx) { + _externalState->startRequest(opCtx); + _refreshUserInfoAsNeeded(opCtx); +} + +Status AuthorizationSessionImpl::addAndAuthorizeUser(OperationContext* opCtx, + const UserName& userName) { + User* user; + AuthorizationManager* authzManager = AuthorizationManager::get(opCtx->getServiceContext()); + Status status = authzManager->acquireUser(opCtx, userName, &user); + if (!status.isOK()) { + return status; + } + + UserHolder userHolder(user, UserReleaser(authzManager)); + + const auto& restrictionSet = userHolder->getRestrictions(); + if (opCtx->getClient() == nullptr) { + return Status(ErrorCodes::AuthenticationFailed, + "Unable to evaluate restrictions, OperationContext has no Client"); + } + + Status restrictionStatus = + restrictionSet.validate(RestrictionEnvironment::get(*opCtx->getClient())); + if (!restrictionStatus.isOK()) { + log() << "Failed to acquire user '" << userName + << "' because of unmet authentication restrictions: " << restrictionStatus.reason(); + return AuthorizationManager::authenticationFailedStatus; + } + + // Calling add() on the UserSet may return a user that was replaced because it was from the + // same database. + userHolder.reset(_authenticatedUsers.add(userHolder.release())); + + // If there are any users and roles in the impersonation data, clear it out. + clearImpersonatedUserData(); + + _buildAuthenticatedRolesVector(); + return Status::OK(); +} + +User* AuthorizationSessionImpl::lookupUser(const UserName& name) { + return _authenticatedUsers.lookup(name); +} + +User* AuthorizationSessionImpl::getSingleUser() { + UserName userName; + + auto userNameItr = getAuthenticatedUserNames(); + if (userNameItr.more()) { + userName = userNameItr.next(); + if (userNameItr.more()) { + uasserted(ErrorCodes::Unauthorized, "too many users are authenticated"); + } + } else { + uasserted(ErrorCodes::Unauthorized, "there are no users authenticated"); + } + + return lookupUser(userName); +} + +void AuthorizationSessionImpl::logoutDatabase(const std::string& dbname) { + User* removedUser = _authenticatedUsers.removeByDBName(dbname); + if (removedUser) { + getAuthorizationManager().releaseUser(removedUser); + } + clearImpersonatedUserData(); + _buildAuthenticatedRolesVector(); +} + +UserNameIterator AuthorizationSessionImpl::getAuthenticatedUserNames() { + return _authenticatedUsers.getNames(); +} + +RoleNameIterator AuthorizationSessionImpl::getAuthenticatedRoleNames() { + return makeRoleNameIterator(_authenticatedRoleNames.begin(), _authenticatedRoleNames.end()); +} + +std::string AuthorizationSessionImpl::getAuthenticatedUserNamesToken() { + std::string ret; + for (UserNameIterator nameIter = getAuthenticatedUserNames(); nameIter.more(); + nameIter.next()) { + ret += '\0'; // Using a NUL byte which isn't valid in usernames to separate them. + ret += nameIter->getFullName(); + } + + return ret; +} + +void AuthorizationSessionImpl::grantInternalAuthorization() { + _authenticatedUsers.add(internalSecurity.user); + _buildAuthenticatedRolesVector(); +} + +PrivilegeVector AuthorizationSessionImpl::getDefaultPrivileges() { + PrivilegeVector defaultPrivileges; + + // If localhost exception is active (and no users exist), + // return a vector of the minimum privileges required to bootstrap + // a system and add the first user. + if (_externalState->shouldAllowLocalhost()) { + ResourcePattern adminDBResource = ResourcePattern::forDatabaseName(ADMIN_DBNAME); + ActionSet setupAdminUserActionSet; + setupAdminUserActionSet.addAction(ActionType::createUser); + setupAdminUserActionSet.addAction(ActionType::grantRole); + Privilege setupAdminUserPrivilege = Privilege(adminDBResource, setupAdminUserActionSet); + + ResourcePattern externalDBResource = ResourcePattern::forDatabaseName("$external"); + Privilege setupExternalUserPrivilege = + Privilege(externalDBResource, ActionType::createUser); + + ActionSet setupServerConfigActionSet; + + // If this server is an arbiter, add specific privileges meant to circumvent + // the behavior of an arbiter in an authenticated replset. See SERVER-5479. + if (_externalState->serverIsArbiter()) { + setupServerConfigActionSet.addAction(ActionType::getCmdLineOpts); + setupServerConfigActionSet.addAction(ActionType::getParameter); + setupServerConfigActionSet.addAction(ActionType::serverStatus); + setupServerConfigActionSet.addAction(ActionType::shutdown); + } + + setupServerConfigActionSet.addAction(ActionType::addShard); + setupServerConfigActionSet.addAction(ActionType::replSetConfigure); + setupServerConfigActionSet.addAction(ActionType::replSetGetStatus); + Privilege setupServerConfigPrivilege = + Privilege(ResourcePattern::forClusterResource(), setupServerConfigActionSet); + + Privilege::addPrivilegeToPrivilegeVector(&defaultPrivileges, setupAdminUserPrivilege); + Privilege::addPrivilegeToPrivilegeVector(&defaultPrivileges, setupExternalUserPrivilege); + Privilege::addPrivilegeToPrivilegeVector(&defaultPrivileges, setupServerConfigPrivilege); + return defaultPrivileges; + } + + return defaultPrivileges; +} + +Status AuthorizationSessionImpl::checkAuthForAggregate(const NamespaceString& nss, + const BSONObj& cmdObj, + bool isMongos) { + if (!nss.isValid()) { + return Status(ErrorCodes::InvalidNamespace, + mongoutils::str::stream() << "Invalid input namespace, " << nss.ns()); + } + + // If this connection does not need to be authenticated (for instance, if auth is disabled), + // return Status::OK() immediately. + if (_externalState->shouldIgnoreAuthChecks()) { + return Status::OK(); + } + + // We require at least one authenticated user when running aggregate with auth enabled. + if (!isAuthenticated()) { + return Status(ErrorCodes::Unauthorized, "unauthorized"); + } + + auto statusWithAggRequest = AggregationRequest::parseFromBSON(nss, cmdObj); + if (!statusWithAggRequest.isOK()) { + return statusWithAggRequest.getStatus(); + } + AggregationRequest aggRequest = std::move(statusWithAggRequest.getValue()); + + const auto& pipeline = aggRequest.getPipeline(); + + // If the aggregation pipeline is empty, confirm the user is authorized for find on 'nss'. + if (pipeline.empty()) { + if (!isAuthorizedForPrivilege( + Privilege(ResourcePattern::forExactNamespace(nss), ActionType::find))) { + return Status(ErrorCodes::Unauthorized, "unauthorized"); + } + + return Status::OK(); + } + + // Confirm the user is authorized for the pipeline's initial document source. We confirm a user + // is authorized incrementally rather than once for the entire pipeline. This will prevent a + // malicious user, who doesn't have access to the initial document source, from consuming the + // resources needed to parse a potentially large pipeline. + auto liteParsedFirstDocumentSource = LiteParsedDocumentSource::parse(aggRequest, pipeline[0]); + if (!liteParsedFirstDocumentSource->isInitialSource() && + !isAuthorizedForPrivilege( + Privilege(ResourcePattern::forExactNamespace(nss), ActionType::find))) { + return Status(ErrorCodes::Unauthorized, "unauthorized"); + } + + // We have done the work to lite parse the first stage. Given that, we check required privileges + // for it using 'liteParsedFirstDocumentSource' regardless of whether is an initial source or + // not. + if (!isAuthorizedForPrivileges(liteParsedFirstDocumentSource->requiredPrivileges(isMongos))) { + return Status(ErrorCodes::Unauthorized, "unauthorized"); + } + + // Confirm privileges for the remainder of the pipepline. Start with the second stage as we have + // already authorized the first. + auto pipelineIter = pipeline.begin() + 1; + + for (; pipelineIter != pipeline.end(); ++pipelineIter) { + auto liteParsedDocSource = LiteParsedDocumentSource::parse(aggRequest, *pipelineIter); + if (!isAuthorizedForPrivileges(liteParsedDocSource->requiredPrivileges(isMongos))) { + return Status(ErrorCodes::Unauthorized, "unauthorized"); + } + } + + return Status::OK(); +} + +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, + const BSONObj& document) { + if (ns.coll() == "system.indexes"_sd) { + BSONElement nsElement = document["ns"]; + if (nsElement.type() != String) { + return Status(nsElement.type() == BSONType::EOO ? ErrorCodes::NoSuchKey + : ErrorCodes::TypeMismatch, + "Cannot authorize inserting into " + "system.indexes documents without a string-typed \"ns\" field."); + } + NamespaceString indexNS(nsElement.valueStringData()); + if (!isAuthorizedForActionsOnNamespace(indexNS, ActionType::createIndex)) { + return Status(ErrorCodes::Unauthorized, + str::stream() << "not authorized to create index on " << indexNS.ns()); + } + } else { + ActionSet required{ActionType::insert}; + if (documentValidationDisabled(opCtx)) { + 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 BSONObj& update, + bool upsert) { + ActionSet required{ActionType::update}; + StringData operationType = "update"_sd; + + if (upsert) { + required.addAction(ActionType::insert); + operationType = "upsert"_sd; + } + + if (documentValidationDisabled(opCtx)) { + 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 (ns.isListCollectionsCursorNS()) { + // listCollections: Target the database being enumerated. + if (isAuthorizedForActionsOnResource(ResourcePattern::forDatabaseName(ns.db()), + ActionType::killAnyCursor)) { + return Status::OK(); + } + const bool canKill = + isAuthorizedForActionsOnResource(ResourcePattern::forDatabaseName(ns.db()), + ActionType::killCursors) || + isAuthorizedToListCollections(ns.db()); + if (canKill && isCoauthorizedWith(cursorOwner)) { + return Status::OK(); + } + return Status(ErrorCodes::Unauthorized, + str::stream() << "not authorized to kill listCollections cursor on " + << ns.ns()); + } else if (ns.isListIndexesCursorNS()) { + + // listIndexes: Target the underlying collection. + NamespaceString targetNS = ns.getTargetNSForListIndexes(); + if (isAuthorizedForActionsOnNamespace(targetNS, ActionType::killAnyCursor)) { + return Status::OK(); + } + const bool canKill = isAuthorizedForActionsOnNamespace(targetNS, ActionType::killCursors) || + isAuthorizedForActionsOnNamespace(targetNS, ActionType::listIndexes); + if (canKill && isCoauthorizedWith(cursorOwner)) { + return Status::OK(); + } + + return Status(ErrorCodes::Unauthorized, + str::stream() << "not authorized to kill listIndexes cursor on " << ns.ns()); + } else { + + // Otherwise: Target the collection as named. + if (isAuthorizedForActionsOnNamespace(ns, ActionType::killAnyCursor)) { + return Status::OK(); + } + const bool canKill = isAuthorizedForActionsOnNamespace(ns, ActionType::killCursors) || + isAuthorizedForActionsOnNamespace(ns, ActionType::find); + if (canKill && isCoauthorizedWith(cursorOwner)) { + return Status::OK(); + } + return Status(ErrorCodes::Unauthorized, + str::stream() << "not authorized to kill cursor on " << ns.ns()); + } +} + +Status AuthorizationSessionImpl::checkAuthForCreate(const NamespaceString& ns, + const BSONObj& cmdObj, + bool isMongos) { + if (cmdObj["capped"].trueValue() && + !isAuthorizedForActionsOnNamespace(ns, ActionType::convertToCapped)) { + return Status(ErrorCodes::Unauthorized, "unauthorized"); + } + + const bool hasCreateCollectionAction = + isAuthorizedForActionsOnNamespace(ns, ActionType::createCollection); + + // If attempting to create a view, check for additional required privileges. + if (cmdObj["viewOn"]) { + // You need the createCollection action on this namespace; the insert action is not + // sufficient. + if (!hasCreateCollectionAction) { + return Status(ErrorCodes::Unauthorized, "unauthorized"); + } + + // Parse the viewOn namespace and the pipeline. If no pipeline was specified, use the empty + // pipeline. + NamespaceString viewOnNs(ns.db(), cmdObj["viewOn"].checkAndGetStringData()); + auto pipeline = + cmdObj.hasField("pipeline") ? BSONArray(cmdObj["pipeline"].Obj()) : BSONArray(); + return checkAuthForCreateOrModifyView(this, ns, viewOnNs, pipeline, isMongos); + } + + // To create a regular collection, ActionType::createCollection or ActionType::insert are + // both acceptable. + if (hasCreateCollectionAction || isAuthorizedForActionsOnNamespace(ns, ActionType::insert)) { + return Status::OK(); + } + + return Status(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; + + uassert(ErrorCodes::InvalidNamespace, + "Failed to parse namespace element", + element.type() == String || isUUID); + + if (isUUID) { + return isAuthorizedForActionsOnResource(ResourcePattern::forClusterResource(), + ActionType::useUUID); + } + + return true; +} + +bool AuthorizationSessionImpl::isAuthorizedToCreateRole( + const struct auth::CreateOrUpdateRoleArgs& args) { + // A user is allowed to create a role under either of two conditions. + + // The user may create a role if the authorization system says they are allowed to. + if (isAuthorizedForActionsOnResource(ResourcePattern::forDatabaseName(args.roleName.getDB()), + ActionType::createRole)) { + return true; + } + + // The user may create a role if the localhost exception is enabled, and they already own the + // role. This implies they have obtained the role through an external authorization mechanism. + if (_externalState->shouldAllowLocalhost()) { + for (const User* const user : _authenticatedUsers) { + if (user->hasRole(args.roleName)) { + return true; + } + } + log() << "Not authorized to create the first role in the system '" << args.roleName + << "' using the localhost exception. The user needs to acquire the role through " + "external authentication first."; + } + + 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; + + return _isAuthorizedForPrivilege(privilege); +} + +bool AuthorizationSessionImpl::isAuthorizedForPrivileges(const vector<Privilege>& privileges) { + if (_externalState->shouldIgnoreAuthChecks()) + return true; + + for (size_t i = 0; i < privileges.size(); ++i) { + if (!_isAuthorizedForPrivilege(privileges[i])) + return false; + } + + return true; +} + +bool AuthorizationSessionImpl::isAuthorizedForActionsOnResource(const ResourcePattern& resource, + ActionType action) { + return isAuthorizedForPrivilege(Privilege(resource, action)); +} + +bool AuthorizationSessionImpl::isAuthorizedForActionsOnResource(const ResourcePattern& resource, + const ActionSet& actions) { + return isAuthorizedForPrivilege(Privilege(resource, actions)); +} + +bool AuthorizationSessionImpl::isAuthorizedForActionsOnNamespace(const NamespaceString& ns, + ActionType action) { + return isAuthorizedForPrivilege(Privilege(ResourcePattern::forExactNamespace(ns), action)); +} + +bool AuthorizationSessionImpl::isAuthorizedForActionsOnNamespace(const NamespaceString& ns, + const ActionSet& actions) { + return isAuthorizedForPrivilege(Privilege(ResourcePattern::forExactNamespace(ns), actions)); +} + +static const int resourceSearchListCapacity = 5; +/** + * Builds from "target" an exhaustive list of all ResourcePatterns that match "target". + * + * Stores the resulting list into resourceSearchList, and returns the length. + * + * The seach lists are as follows, depending on the type of "target": + * + * target is ResourcePattern::forAnyResource(): + * searchList = { ResourcePattern::forAnyResource(), ResourcePattern::forAnyResource() } + * target is the ResourcePattern::forClusterResource(): + * searchList = { ResourcePattern::forAnyResource(), ResourcePattern::forClusterResource() } + * target is a database, db: + * searchList = { ResourcePattern::forAnyResource(), + * ResourcePattern::forAnyNormalResource(), + * db } + * target is a non-system collection, db.coll: + * searchList = { ResourcePattern::forAnyResource(), + * ResourcePattern::forAnyNormalResource(), + * db, + * coll, + * db.coll } + * target is a system collection, db.system.coll: + * searchList = { ResourcePattern::forAnyResource(), + * system.coll, + * db.system.coll } + */ +static int buildResourceSearchList(const ResourcePattern& target, + ResourcePattern resourceSearchList[resourceSearchListCapacity]) { + int size = 0; + resourceSearchList[size++] = ResourcePattern::forAnyResource(); + if (target.isExactNamespacePattern()) { + if (!target.ns().isSystem()) { + // Some databases should not be matchable with ResourcePattern::forAnyNormalResource. + // 'local' and 'config' are used to store special system collections, which user level + // administrators should not be able to manipulate. + if (target.ns().db() != "local" && target.ns().db() != "config") { + resourceSearchList[size++] = ResourcePattern::forAnyNormalResource(); + } + resourceSearchList[size++] = ResourcePattern::forDatabaseName(target.ns().db()); + } + resourceSearchList[size++] = ResourcePattern::forCollectionName(target.ns().coll()); + } else if (target.isDatabasePattern()) { + resourceSearchList[size++] = ResourcePattern::forAnyNormalResource(); + } + resourceSearchList[size++] = target; + dassert(size <= resourceSearchListCapacity); + return size; +} + +bool AuthorizationSessionImpl::isAuthorizedToChangeAsUser(const UserName& userName, + ActionType actionType) { + User* user = lookupUser(userName); + if (!user) { + return false; + } + ResourcePattern resourceSearchList[resourceSearchListCapacity]; + const int resourceSearchListLength = buildResourceSearchList( + ResourcePattern::forDatabaseName(userName.getDB()), resourceSearchList); + + ActionSet actions; + for (int i = 0; i < resourceSearchListLength; ++i) { + actions.addAllActionsFromSet(user->getActionsForResource(resourceSearchList[i])); + } + 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); +} + +bool AuthorizationSessionImpl::isAuthorizedToListCollections(StringData dbname) { + // Check for the listCollections ActionType on the database or find on system.namespaces for + // pre 3.0 systems. + + return AuthorizationSessionImpl::isAuthorizedForActionsOnResource( + ResourcePattern::forDatabaseName(dbname), ActionType::listCollections) || + AuthorizationSessionImpl::isAuthorizedForActionsOnResource( + ResourcePattern::forExactNamespace(NamespaceString(dbname, "system.namespaces")), + ActionType::find); +} + +bool AuthorizationSessionImpl::isAuthenticatedAsUserWithRole(const RoleName& roleName) { + for (UserSet::iterator it = _authenticatedUsers.begin(); it != _authenticatedUsers.end(); + ++it) { + if ((*it)->hasRole(roleName)) { + return true; + } + } + return false; +} + +bool AuthorizationSessionImpl::isAuthenticated() { + return _authenticatedUsers.begin() != _authenticatedUsers.end(); +} + +void AuthorizationSessionImpl::_refreshUserInfoAsNeeded(OperationContext* opCtx) { + AuthorizationManager& authMan = getAuthorizationManager(); + UserSet::iterator it = _authenticatedUsers.begin(); + while (it != _authenticatedUsers.end()) { + User* user = *it; + + if (!user->isValid()) { + // Make a good faith effort to acquire an up-to-date user object, since the one + // we've cached is marked "out-of-date." + UserName name = user->getName(); + User* updatedUser; + + Status status = authMan.acquireUser(opCtx, name, &updatedUser); + switch (status.code()) { + case ErrorCodes::OK: { + + // Verify the updated user object's authentication restrictions. + UserHolder userHolder(user, UserReleaser(&authMan, &_authenticatedUsers, it)); + UserHolder updatedUserHolder(updatedUser, UserReleaser(&authMan)); + try { + const auto& restrictionSet = + updatedUserHolder->getRestrictions(); // Owned by updatedUser + invariant(opCtx->getClient()); + Status restrictionStatus = restrictionSet.validate( + RestrictionEnvironment::get(*opCtx->getClient())); + if (!restrictionStatus.isOK()) { + log() << "Removed user " << name + << " with unmet authentication restrictions from session cache of" + << " user information. Restriction failed because: " + << restrictionStatus.reason(); + // If we remove from the UserSet, we cannot increment the iterator. + continue; + } + } catch (...) { + log() << "Evaluating authentication restrictions for " << name + << " resulted in an unknown exception. Removing user from the" + << " session cache."; + continue; + } + + // Success! Replace the old User object with the updated one. + fassert(17067, + _authenticatedUsers.replaceAt(it, updatedUserHolder.release()) == + userHolder.get()); + LOG(1) << "Updated session cache of user information for " << name; + break; + } + case ErrorCodes::UserNotFound: { + // User does not exist anymore; remove it from _authenticatedUsers. + fassert(17068, _authenticatedUsers.removeAt(it) == user); + authMan.releaseUser(user); + log() << "Removed deleted user " << name + << " from session cache of user information."; + continue; // No need to advance "it" in this case. + } + case ErrorCodes::UnsupportedFormat: { + // An auth subsystem has explicitly indicated a failure. + fassert(40555, _authenticatedUsers.removeAt(it) == user); + authMan.releaseUser(user); + log() << "Removed user " << name + << " from session cache of user information because of refresh failure:" + << " '" << status << "'."; + continue; // No need to advance "it" in this case. + } + default: + // Unrecognized error; assume that it's transient, and continue working with the + // out-of-date privilege data. + warning() << "Could not fetch updated user privilege information for " << name + << "; continuing to use old information. Reason is " + << redact(status); + break; + } + } + ++it; + } + _buildAuthenticatedRolesVector(); +} + +void AuthorizationSessionImpl::_buildAuthenticatedRolesVector() { + _authenticatedRoleNames.clear(); + for (UserSet::iterator it = _authenticatedUsers.begin(); it != _authenticatedUsers.end(); + ++it) { + RoleNameIterator roles = (*it)->getIndirectRoles(); + while (roles.more()) { + RoleName roleName = roles.next(); + _authenticatedRoleNames.push_back(RoleName(roleName.getRole(), roleName.getDB())); + } + } +} + +bool AuthorizationSessionImpl::_isAuthorizedForPrivilege(const Privilege& privilege) { + const ResourcePattern& target(privilege.getResourcePattern()); + + ResourcePattern resourceSearchList[resourceSearchListCapacity]; + const int resourceSearchListLength = buildResourceSearchList(target, resourceSearchList); + + ActionSet unmetRequirements = privilege.getActions(); + + PrivilegeVector defaultPrivileges = getDefaultPrivileges(); + for (PrivilegeVector::iterator it = defaultPrivileges.begin(); it != defaultPrivileges.end(); + ++it) { + for (int i = 0; i < resourceSearchListLength; ++i) { + if (!(it->getResourcePattern() == resourceSearchList[i])) + continue; + + ActionSet userActions = it->getActions(); + unmetRequirements.removeAllActionsFromSet(userActions); + + if (unmetRequirements.empty()) + return true; + } + } + + for (UserSet::iterator it = _authenticatedUsers.begin(); it != _authenticatedUsers.end(); + ++it) { + User* user = *it; + for (int i = 0; i < resourceSearchListLength; ++i) { + ActionSet userActions = user->getActionsForResource(resourceSearchList[i]); + unmetRequirements.removeAllActionsFromSet(userActions); + + if (unmetRequirements.empty()) + return true; + } + } + + return false; +} + +void AuthorizationSessionImpl::setImpersonatedUserData(std::vector<UserName> usernames, + std::vector<RoleName> roles) { + _impersonatedUserNames = usernames; + _impersonatedRoleNames = roles; + _impersonationFlag = true; +} + +bool AuthorizationSessionImpl::isCoauthorizedWithClient(Client* opClient) { + auto getUserNames = [](AuthorizationSession* authSession) { + if (authSession->isImpersonating()) { + return authSession->getImpersonatedUserNames(); + } else { + return authSession->getAuthenticatedUserNames(); + } + }; + + UserNameIterator it = getUserNames(this); + while (it.more()) { + UserNameIterator opIt = getUserNames(AuthorizationSession::get(opClient)); + while (opIt.more()) { + if (it.get() == opIt.get()) { + return true; + } + opIt.next(); + } + it.next(); + } + + return false; +} + +bool AuthorizationSessionImpl::isCoauthorizedWith(UserNameIterator userNameIter) { + if (!getAuthorizationManager().isAuthEnabled()) { + return true; + } + if (!userNameIter.more() && !isAuthenticated()) { + return true; + } + + for (; userNameIter.more(); userNameIter.next()) { + for (UserNameIterator thisUserNameIter = getAuthenticatedUserNames(); + thisUserNameIter.more(); + thisUserNameIter.next()) { + if (*userNameIter == *thisUserNameIter) { + return true; + } + } + } + + return false; +} + +UserNameIterator AuthorizationSessionImpl::getImpersonatedUserNames() { + return makeUserNameIterator(_impersonatedUserNames.begin(), _impersonatedUserNames.end()); +} + +RoleNameIterator AuthorizationSessionImpl::getImpersonatedRoleNames() { + return makeRoleNameIterator(_impersonatedRoleNames.begin(), _impersonatedRoleNames.end()); +} + +bool AuthorizationSessionImpl::isUsingLocalhostBypass() { + return getAuthorizationManager().isAuthEnabled() && _externalState->shouldAllowLocalhost(); +} + +// Clear the vectors of impersonated usernames and roles. +void AuthorizationSessionImpl::clearImpersonatedUserData() { + _impersonatedUserNames.clear(); + _impersonatedRoleNames.clear(); + _impersonationFlag = false; +} + + +bool AuthorizationSessionImpl::isImpersonating() const { + return _impersonationFlag; +} + +auto AuthorizationSessionImpl::checkCursorSessionPrivilege( + OperationContext* const opCtx, const boost::optional<LogicalSessionId> cursorSessionId) + -> Status { + auto nobodyIsLoggedIn = [authSession = this] { + return !authSession->isAuthenticated(); + }; + + auto authHasImpersonatePrivilege = [authSession = this] { + return authSession->isAuthorizedForPrivilege( + Privilege(ResourcePattern::forClusterResource(), ActionType::impersonate)); + }; + + auto authIsOn = [authSession = this] { + return authSession->getAuthorizationManager().isAuthEnabled(); + }; + + auto sessionIdToStringOrNone = + [](const boost::optional<LogicalSessionId>& sessionId) -> std::string { + if (sessionId) { + return str::stream() << *sessionId; + } + return "none"; + }; + + // If the cursor has a session then one of the following must be true: + // 1: context session id must match cursor session id. + // 2: user must be magic special (__system, or background task, etc). + + // We do not check the user's ID against the cursor's notion of a user ID, since higher level + // auth checks will check that for us anyhow. + if (authIsOn() && // If the authorization is not on, then we permit anybody to do anything. + cursorSessionId != opCtx->getLogicalSessionId() && // If the cursor's session doesn't match + // the Operation Context's session, then + // we should forbid the operation even + // when the cursor has no session. + !nobodyIsLoggedIn() && // Unless, for some reason a user isn't actually using this + // Operation Context (which implies a background job + !authHasImpersonatePrivilege() // Or if the user has an impersonation privilege, in which + // case, the user gets to sidestep certain checks. + ) { + return Status{ErrorCodes::Unauthorized, + str::stream() << "Cursor session id (" + << sessionIdToStringOrNone(cursorSessionId) + << ") is not the same as the operation context's session id (" + << sessionIdToStringOrNone(opCtx->getLogicalSessionId()) + << ")"}; + } + + return Status::OK(); +} + +} // namespace mongo diff --git a/src/mongo/db/auth/authorization_session_impl.h b/src/mongo/db/auth/authorization_session_impl.h new file mode 100644 index 00000000000..d11dbb3e348 --- /dev/null +++ b/src/mongo/db/auth/authorization_session_impl.h @@ -0,0 +1,232 @@ +/** + * Copyright (C) 2018 10gen 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 "authorization_session.h" + +#include <memory> +#include <string> +#include <vector> + +#include "mongo/base/disallow_copying.h" +#include "mongo/base/status.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/authz_session_external_state.h" +#include "mongo/db/auth/privilege.h" +#include "mongo/db/auth/user_name.h" +#include "mongo/db/auth/user_set.h" +#include "mongo/db/namespace_string.h" + +namespace mongo { + +namespace auth { + +struct CreateOrUpdateRoleArgs; +} +class Client; + +/** + * Contains all the authorization logic for a single client connection. It contains a set of + * the users which have been authenticated, as well as a set of privileges that have been + * granted to those users to perform various actions. + * + * An AuthorizationSession object is present within every mongo::Client object. + * + * Users in the _authenticatedUsers cache may get marked as invalid by the AuthorizationManager, + * for instance if their privileges are changed by a user or role modification command. At the + * beginning of every user-initiated operation startRequest() gets called which updates + * the cached information about any users who have been marked as invalid. This guarantees that + * every operation looks at one consistent view of each user for every auth check required over + * the lifetime of the operation. + */ +class AuthorizationSessionImpl : public AuthorizationSession { +public: + struct InstallMockForTestingOrAuthImpl { + explicit InstallMockForTestingOrAuthImpl() = default; + }; + explicit AuthorizationSessionImpl(std::unique_ptr<AuthzSessionExternalState> externalState, + InstallMockForTestingOrAuthImpl); + + ~AuthorizationSessionImpl() override; + + AuthorizationManager& getAuthorizationManager() override; + + void startRequest(OperationContext* opCtx) override; + + Status addAndAuthorizeUser(OperationContext* opCtx, const UserName& userName) override; + + User* lookupUser(const UserName& name) override; + + bool isAuthenticated() override; + + User* getSingleUser() override; + + UserNameIterator getAuthenticatedUserNames() override; + + RoleNameIterator getAuthenticatedRoleNames() override; + + std::string getAuthenticatedUserNamesToken() override; + + void logoutDatabase(const std::string& dbname) override; + + void grantInternalAuthorization() override; + + PrivilegeVector getDefaultPrivileges() override; + + Status checkAuthForFind(const NamespaceString& ns, bool hasTerm) override; + + Status checkAuthForGetMore(const NamespaceString& ns, + long long cursorID, + bool hasTerm) override; + + Status checkAuthForUpdate(OperationContext* opCtx, + const NamespaceString& ns, + const BSONObj& query, + const BSONObj& update, + bool upsert) override; + + Status checkAuthForInsert(OperationContext* opCtx, + const NamespaceString& ns, + const BSONObj& document) override; + + Status checkAuthForDelete(OperationContext* opCtx, + const NamespaceString& ns, + const BSONObj& query) override; + + Status checkAuthForKillCursors(const NamespaceString& cursorNss, + UserNameIterator cursorOwner) override; + + Status checkAuthForAggregate(const NamespaceString& ns, + const BSONObj& cmdObj, + bool isMongos) override; + + Status checkAuthForCreate(const NamespaceString& ns, + const BSONObj& cmdObj, + bool isMongos) override; + + Status checkAuthForCollMod(const NamespaceString& ns, + const BSONObj& cmdObj, + bool isMongos) override; + + Status checkAuthorizedToGrantPrivilege(const Privilege& privilege) override; + + Status checkAuthorizedToRevokePrivilege(const Privilege& privilege) override; + + bool isUsingLocalhostBypass() override; + + bool isAuthorizedToParseNamespaceElement(const BSONElement& elem) override; + + bool isAuthorizedToCreateRole(const auth::CreateOrUpdateRoleArgs& args) override; + + bool isAuthorizedToGrantRole(const RoleName& role) override; + + bool isAuthorizedToRevokeRole(const RoleName& role) override; + + bool isAuthorizedToChangeAsUser(const UserName& userName, ActionType actionType) override; + + bool isAuthorizedToChangeOwnPasswordAsUser(const UserName& userName) override; + + bool isAuthorizedToListCollections(StringData dbname) override; + + bool isAuthorizedToChangeOwnCustomDataAsUser(const UserName& userName) override; + + bool isAuthenticatedAsUserWithRole(const RoleName& roleName) override; + + bool isAuthorizedForPrivilege(const Privilege& privilege) override; + + bool isAuthorizedForPrivileges(const std::vector<Privilege>& privileges) override; + + bool isAuthorizedForActionsOnResource(const ResourcePattern& resource, + ActionType action) override; + + bool isAuthorizedForActionsOnResource(const ResourcePattern& resource, + const ActionSet& actions) override; + + bool isAuthorizedForActionsOnNamespace(const NamespaceString& ns, ActionType action) override; + + bool isAuthorizedForActionsOnNamespace(const NamespaceString& ns, + const ActionSet& actions) override; + + void setImpersonatedUserData(std::vector<UserName> usernames, + std::vector<RoleName> roles) override; + + UserNameIterator getImpersonatedUserNames() override; + + RoleNameIterator getImpersonatedRoleNames() override; + + void clearImpersonatedUserData() override; + + bool isCoauthorizedWithClient(Client* opClient) override; + + bool isCoauthorizedWith(UserNameIterator userNameIter) override; + + bool isImpersonating() const override; + + Status checkCursorSessionPrivilege(OperationContext* const opCtx, + boost::optional<LogicalSessionId> cursorSessionId) override; + +protected: + // Builds a vector of all roles held by users who are authenticated on this connection. The + // vector is stored in _authenticatedRoleNames. This function is called when users are + // logged in or logged out, as well as when the user cache is determined to be out of date. + void _buildAuthenticatedRolesVector(); + + // All Users who have been authenticated on this connection. + UserSet _authenticatedUsers; + + // The roles of the authenticated users. This vector is generated when the authenticated + // users set is changed. + std::vector<RoleName> _authenticatedRoleNames; + +private: + // If any users authenticated on this session are marked as invalid this updates them with + // up-to-date information. May require a read lock on the "admin" db to read the user data. + void _refreshUserInfoAsNeeded(OperationContext* opCtx); + + + // Checks if this connection is authorized for the given Privilege, ignoring whether or not + // we should even be doing authorization checks in general. Note: this may acquire a read + // lock on the admin database (to update out-of-date user privilege information). + bool _isAuthorizedForPrivilege(const Privilege& privilege); + + std::tuple<std::vector<UserName>*, std::vector<RoleName>*> _getImpersonations() override { + return std::make_tuple(&_impersonatedUserNames, &_impersonatedRoleNames); + } + + std::unique_ptr<AuthzSessionExternalState> _externalState; + + // A vector of impersonated UserNames and a vector of those users' RoleNames. + // These are used in the auditing system. They are not used for authz checks. + std::vector<UserName> _impersonatedUserNames; + std::vector<RoleName> _impersonatedRoleNames; + bool _impersonationFlag; +}; +} // namespace mongo diff --git a/src/mongo/db/auth/authorization_session_test.cpp b/src/mongo/db/auth/authorization_session_test.cpp index 7933bb8f46b..2ee3659629f 100644 --- a/src/mongo/db/auth/authorization_session_test.cpp +++ b/src/mongo/db/auth/authorization_session_test.cpp @@ -37,6 +37,7 @@ #include "mongo/crypto/sha256_block.h" #include "mongo/db/auth/action_type.h" #include "mongo/db/auth/authorization_manager.h" +#include "mongo/db/auth/authorization_manager_impl.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" @@ -105,13 +106,16 @@ public: auto localManagerState = stdx::make_unique<FailureCapableAuthzManagerExternalStateMock>(); managerState = localManagerState.get(); managerState->setAuthzVersion(AuthorizationManager::schemaVersion26Final); - auto uniqueAuthzManager = - stdx::make_unique<AuthorizationManager>(std::move(localManagerState)); + auto uniqueAuthzManager = std::make_unique<AuthorizationManagerImpl>( + std::move(localManagerState), + AuthorizationManagerImpl::InstallMockForTestingOrAuthImpl{}); authzManager = uniqueAuthzManager.get(); AuthorizationManager::set(&serviceContext, std::move(uniqueAuthzManager)); - auto localSessionState = stdx::make_unique<AuthzSessionExternalStateMock>(authzManager); + auto localSessionState = std::make_unique<AuthzSessionExternalStateMock>(authzManager); sessionState = localSessionState.get(); - authzSession = stdx::make_unique<AuthorizationSessionForTest>(std::move(localSessionState)); + authzSession = std::make_unique<AuthorizationSessionForTest>( + std::move(localSessionState), + AuthorizationSessionImpl::InstallMockForTestingOrAuthImpl{}); authzManager->setAuthEnabled(true); credentials = diff --git a/src/mongo/db/auth/authz_manager_external_state.cpp b/src/mongo/db/auth/authz_manager_external_state.cpp index f0a30f51a9d..1eef2c854d2 100644 --- a/src/mongo/db/auth/authz_manager_external_state.cpp +++ b/src/mongo/db/auth/authz_manager_external_state.cpp @@ -35,7 +35,7 @@ namespace mongo { -stdx::function<std::unique_ptr<AuthzManagerExternalState>()> AuthzManagerExternalState::create; +MONGO_DEFINE_SHIM(AuthzManagerExternalState::create); AuthzManagerExternalState::AuthzManagerExternalState() = default; AuthzManagerExternalState::~AuthzManagerExternalState() = default; diff --git a/src/mongo/db/auth/authz_manager_external_state.h b/src/mongo/db/auth/authz_manager_external_state.h index 51d2e7a4808..83ba92fd037 100644 --- a/src/mongo/db/auth/authz_manager_external_state.h +++ b/src/mongo/db/auth/authz_manager_external_state.h @@ -33,6 +33,7 @@ #include <vector> #include "mongo/base/disallow_copying.h" +#include "mongo/base/shim.h" #include "mongo/base/status.h" #include "mongo/db/auth/authorization_manager.h" #include "mongo/db/auth/privilege_format.h" @@ -53,10 +54,11 @@ class OperationContext; * easier to test as well as to allow different implementations for mongos and mongod. */ class AuthzManagerExternalState { - MONGO_DISALLOW_COPYING(AuthzManagerExternalState); + AuthzManagerExternalState(const AuthzManagerExternalState&) = delete; + AuthzManagerExternalState& operator=(const AuthzManagerExternalState&) = delete; public: - static stdx::function<std::unique_ptr<AuthzManagerExternalState>()> create; + static MONGO_DECLARE_SHIM(()->std::unique_ptr<AuthzManagerExternalState>) create; virtual ~AuthzManagerExternalState(); diff --git a/src/mongo/db/auth/authz_manager_external_state_d.cpp b/src/mongo/db/auth/authz_manager_external_state_d.cpp index 0e259a9d11c..1454076a12b 100644 --- a/src/mongo/db/auth/authz_manager_external_state_d.cpp +++ b/src/mongo/db/auth/authz_manager_external_state_d.cpp @@ -91,4 +91,9 @@ Status AuthzManagerExternalStateMongod::findOne(OperationContext* opCtx, << query); } +MONGO_REGISTER_SHIM(AuthzManagerExternalState::create) +()->std::unique_ptr<AuthzManagerExternalState> { + return std::make_unique<AuthzManagerExternalStateMongod>(); +} + } // namespace mongo diff --git a/src/mongo/db/auth/authz_manager_external_state_mock.cpp b/src/mongo/db/auth/authz_manager_external_state_mock.cpp index fec42b1bf88..d755d61c556 100644 --- a/src/mongo/db/auth/authz_manager_external_state_mock.cpp +++ b/src/mongo/db/auth/authz_manager_external_state_mock.cpp @@ -47,6 +47,12 @@ #include "mongo/util/mongoutils/str.h" namespace mongo { + +MONGO_REGISTER_SHIM(AuthzManagerExternalState::create) +()->std::unique_ptr<AuthzManagerExternalState> { + return std::make_unique<AuthzManagerExternalStateMock>(); +} + namespace { void addRoleNameToObjectElement(mutablebson::Element object, const RoleName& role) { fassert(17175, object.appendString(AuthorizationManager::ROLE_NAME_FIELD_NAME, role.getRole())); diff --git a/src/mongo/db/auth/authz_manager_external_state_s.cpp b/src/mongo/db/auth/authz_manager_external_state_s.cpp index 14fef957ec9..9514a58c33b 100644 --- a/src/mongo/db/auth/authz_manager_external_state_s.cpp +++ b/src/mongo/db/auth/authz_manager_external_state_s.cpp @@ -51,6 +51,12 @@ #include "mongo/util/stringutils.h" namespace mongo { + +MONGO_REGISTER_SHIM(AuthzManagerExternalState::create) +()->std::unique_ptr<AuthzManagerExternalState> { + return std::make_unique<AuthzManagerExternalStateMongos>(); +} + namespace { /** diff --git a/src/mongo/db/auth/authz_session_external_state.cpp b/src/mongo/db/auth/authz_session_external_state.cpp index edc3a589d88..5985a1d54fe 100644 --- a/src/mongo/db/auth/authz_session_external_state.cpp +++ b/src/mongo/db/auth/authz_session_external_state.cpp @@ -45,4 +45,6 @@ AuthorizationManager& AuthzSessionExternalState::getAuthorizationManager() { return *_authzManager; } +MONGO_DEFINE_SHIM(AuthzSessionExternalState::create); + } // namespace mongo diff --git a/src/mongo/db/auth/authz_session_external_state.h b/src/mongo/db/auth/authz_session_external_state.h index ba43be8050b..bd8cde24fea 100644 --- a/src/mongo/db/auth/authz_session_external_state.h +++ b/src/mongo/db/auth/authz_session_external_state.h @@ -47,9 +47,13 @@ class OperationContext; * easier to test as well as to allow different implementations in mongos and mongod. */ class AuthzSessionExternalState { - MONGO_DISALLOW_COPYING(AuthzSessionExternalState); + AuthzSessionExternalState(const AuthzSessionExternalState&) = delete; + AuthzSessionExternalState& operator=(const AuthzSessionExternalState&) = delete; public: + static MONGO_DECLARE_SHIM( + (AuthorizationManager * authzManager)->std::unique_ptr<AuthzSessionExternalState>) create; + virtual ~AuthzSessionExternalState(); AuthorizationManager& getAuthorizationManager(); diff --git a/src/mongo/db/auth/authz_session_external_state_d.cpp b/src/mongo/db/auth/authz_session_external_state_d.cpp index 0c8689feb10..5c5ef29f899 100644 --- a/src/mongo/db/auth/authz_session_external_state_d.cpp +++ b/src/mongo/db/auth/authz_session_external_state_d.cpp @@ -66,4 +66,9 @@ bool AuthzSessionExternalStateMongod::serverIsArbiter() const { repl::ReplicationCoordinator::get(getGlobalServiceContext())->getMemberState().arbiter()); } +MONGO_REGISTER_SHIM(AuthzSessionExternalState::create) +(AuthorizationManager* const authzManager)->std::unique_ptr<AuthzSessionExternalState> { + return std::make_unique<AuthzSessionExternalStateMongod>(authzManager); +} + } // namespace mongo diff --git a/src/mongo/db/authz_manager_external_state_factory_d.cpp b/src/mongo/db/auth/authz_session_external_state_mock.cpp index 12a9ec19c43..34bf3855204 100644 --- a/src/mongo/db/authz_manager_external_state_factory_d.cpp +++ b/src/mongo/db/auth/authz_session_external_state_mock.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) 2008-2014 MongoDB Inc. + * Copyright (C) 2018 10gen 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, @@ -26,27 +26,11 @@ * it in the license file. */ -#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kCommand - -#include "mongo/platform/basic.h" - -#include "mongo/base/init.h" -#include "mongo/db/auth/authz_manager_external_state_d.h" -#include "mongo/stdx/memory.h" +#include "mongo/db/auth/authz_session_external_state_mock.h" namespace mongo { -using std::unique_ptr; - -namespace { - -unique_ptr<AuthzManagerExternalState> createAuthzManagerExternalStateMongod() { - return stdx::make_unique<AuthzManagerExternalStateMongod>(); +MONGO_REGISTER_SHIM(AuthzSessionExternalState::create) +(AuthorizationManager* const authzManager)->std::unique_ptr<AuthzSessionExternalState> { + return std::make_unique<AuthzSessionExternalStateMock>(authzManager); } - -MONGO_INITIALIZER(CreateAuthorizationExternalStateFactory)(InitializerContext* context) { - AuthzManagerExternalState::create = &createAuthzManagerExternalStateMongod; - return Status::OK(); -} - -} // namespace } // namespace mongo diff --git a/src/mongo/db/auth/authz_session_external_state_mock.h b/src/mongo/db/auth/authz_session_external_state_mock.h index d577d7b2e4c..0713d18f6dd 100644 --- a/src/mongo/db/auth/authz_session_external_state_mock.h +++ b/src/mongo/db/auth/authz_session_external_state_mock.h @@ -39,7 +39,8 @@ namespace mongo { * Mock of the AuthzSessionExternalState class used only for testing. */ class AuthzSessionExternalStateMock : public AuthzSessionExternalState { - MONGO_DISALLOW_COPYING(AuthzSessionExternalStateMock); + AuthzSessionExternalStateMock(const AuthzSessionExternalStateMock&) = delete; + AuthzSessionExternalStateMock& operator=(const AuthzSessionExternalStateMock&) = delete; public: AuthzSessionExternalStateMock(AuthorizationManager* authzManager) diff --git a/src/mongo/db/auth/authz_session_external_state_s.cpp b/src/mongo/db/auth/authz_session_external_state_s.cpp index 47ac9598735..8f42eb3b30d 100644 --- a/src/mongo/db/auth/authz_session_external_state_s.cpp +++ b/src/mongo/db/auth/authz_session_external_state_s.cpp @@ -46,4 +46,9 @@ void AuthzSessionExternalStateMongos::startRequest(OperationContext* opCtx) { _checkShouldAllowLocalhost(opCtx); } +MONGO_REGISTER_SHIM(AuthzSessionExternalState::create) +(AuthorizationManager* const authzManager)->std::unique_ptr<AuthzSessionExternalState> { + return std::make_unique<AuthzSessionExternalStateMongos>(authzManager); +} + } // namespace mongo diff --git a/src/mongo/db/auth/sasl_authentication_session_test.cpp b/src/mongo/db/auth/sasl_authentication_session_test.cpp index 4babe8c816b..8497edcac37 100644 --- a/src/mongo/db/auth/sasl_authentication_session_test.cpp +++ b/src/mongo/db/auth/sasl_authentication_session_test.cpp @@ -13,6 +13,7 @@ #include "mongo/client/sasl_client_session.h" #include "mongo/crypto/mechanism_scram.h" #include "mongo/db/auth/authorization_manager.h" +#include "mongo/db/auth/authorization_manager_impl.h" #include "mongo/db/auth/authorization_session.h" #include "mongo/db/auth/authz_manager_external_state_mock.h" #include "mongo/db/auth/authz_session_external_state_mock.h" @@ -68,8 +69,9 @@ SaslConversation::SaslConversation(std::string mech) : opClient(serviceContext.makeClient("saslTest")), opCtx(serviceContext.makeOperationContext(opClient.get())), authManagerExternalState(new AuthzManagerExternalStateMock), - authManager(new AuthorizationManager( - std::unique_ptr<AuthzManagerExternalState>(authManagerExternalState))), + authManager(new AuthorizationManagerImpl( + std::unique_ptr<AuthzManagerExternalState>(authManagerExternalState), + AuthorizationManagerImpl::InstallMockForTestingOrAuthImpl{})), authSession(authManager->makeAuthorizationSession()), mechanism(mech) { diff --git a/src/mongo/db/auth/sasl_mechanism_registry_test.cpp b/src/mongo/db/auth/sasl_mechanism_registry_test.cpp index fba7482b033..bdce3b21199 100644 --- a/src/mongo/db/auth/sasl_mechanism_registry_test.cpp +++ b/src/mongo/db/auth/sasl_mechanism_registry_test.cpp @@ -29,6 +29,7 @@ #include "mongo/db/auth/sasl_mechanism_registry.h" #include "mongo/crypto/mechanism_scram.h" #include "mongo/db/auth/authorization_manager.h" +#include "mongo/db/auth/authorization_manager_impl.h" #include "mongo/db/auth/authz_manager_external_state_mock.h" #include "mongo/db/operation_context_noop.h" #include "mongo/db/service_context_noop.h" @@ -135,8 +136,9 @@ public: : opClient(serviceContext.makeClient("mechanismRegistryTest")), opCtx(serviceContext.makeOperationContext(opClient.get())), authManagerExternalState(new AuthzManagerExternalStateMock()), - authManager(new AuthorizationManager( - std::unique_ptr<AuthzManagerExternalStateMock>(authManagerExternalState))) { + authManager(new AuthorizationManagerImpl( + std::unique_ptr<AuthzManagerExternalStateMock>(authManagerExternalState), + AuthorizationManagerImpl::InstallMockForTestingOrAuthImpl{})) { AuthorizationManager::set(&serviceContext, std::unique_ptr<AuthorizationManager>(authManager)); diff --git a/src/mongo/db/auth/sasl_scram_test.cpp b/src/mongo/db/auth/sasl_scram_test.cpp index 3ec3cb5c5d9..2a347b6d464 100644 --- a/src/mongo/db/auth/sasl_scram_test.cpp +++ b/src/mongo/db/auth/sasl_scram_test.cpp @@ -36,7 +36,9 @@ #include "mongo/crypto/sha1_block.h" #include "mongo/crypto/sha256_block.h" #include "mongo/db/auth/authorization_manager.h" +#include "mongo/db/auth/authorization_manager_impl.h" #include "mongo/db/auth/authorization_session.h" +#include "mongo/db/auth/authorization_session_impl.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/sasl_mechanism_registry.h" @@ -189,15 +191,18 @@ protected: opCtx = serviceContext->makeOperationContext(client.get()); auto uniqueAuthzManagerExternalStateMock = - stdx::make_unique<AuthzManagerExternalStateMock>(); + std::make_unique<AuthzManagerExternalStateMock>(); authzManagerExternalState = uniqueAuthzManagerExternalStateMock.get(); - authzManager = new AuthorizationManager(std::move(uniqueAuthzManagerExternalStateMock)); - authzSession = stdx::make_unique<AuthorizationSession>( - stdx::make_unique<AuthzSessionExternalStateMock>(authzManager)); - AuthorizationManager::set(serviceContext.get(), - std::unique_ptr<AuthorizationManager>(authzManager)); - - saslClientSession = stdx::make_unique<NativeSaslClientSession>(); + auto newManager = std::make_unique<AuthorizationManagerImpl>( + std::move(uniqueAuthzManagerExternalStateMock), + AuthorizationManagerImpl::InstallMockForTestingOrAuthImpl{}); + authzSession = std::make_unique<AuthorizationSessionImpl>( + std::make_unique<AuthzSessionExternalStateMock>(newManager.get()), + AuthorizationSessionImpl::InstallMockForTestingOrAuthImpl{}); + authzManager = newManager.get(); + AuthorizationManager::set(serviceContext.get(), std::move(newManager)); + + saslClientSession = std::make_unique<NativeSaslClientSession>(); saslClientSession->setParameter(NativeSaslClientSession::parameterMechanism, saslServerSession->mechanismName()); saslClientSession->setParameter(NativeSaslClientSession::parameterServiceName, "mongodb"); diff --git a/src/mongo/db/auth/user.cpp b/src/mongo/db/auth/user.cpp index c233e8a1d8b..5dc6ac9cf90 100644 --- a/src/mongo/db/auth/user.cpp +++ b/src/mongo/db/auth/user.cpp @@ -1,5 +1,4 @@ /* Copyright 2013 10gen 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, @@ -77,14 +76,6 @@ const User::SCRAMCredentials<SHA256Block>& User::CredentialData::scram<SHA256Blo return scram_sha256; } -const UserName& User::getName() const { - return _name; -} - -const SHA256Block& User::getDigest() const { - return _digest; -} - RoleNameIterator User::getRoles() const { return makeRoleNameIteratorForContainer(_roles); } diff --git a/src/mongo/db/auth/user.h b/src/mongo/db/auth/user.h index 3af4f6abbf3..5fb67ccd08c 100644 --- a/src/mongo/db/auth/user.h +++ b/src/mongo/db/auth/user.h @@ -106,12 +106,17 @@ public: /** * Returns the user name for this user. */ - const UserName& getName() const; + const UserName& getName() const { + return _name; + } /** * Returns a digest of the user's identity */ - const SHA256Block& getDigest() const; + const SHA256Block& getDigest() const { + return _digest; + } + /** * Returns an iterator over the names of the user's direct roles diff --git a/src/mongo/db/catalog/SConscript b/src/mongo/db/catalog/SConscript index 47c0b0f662e..259ea12ab77 100644 --- a/src/mongo/db/catalog/SConscript +++ b/src/mongo/db/catalog/SConscript @@ -34,6 +34,7 @@ env.CppUnitTest( source = 'collection_test.cpp', LIBDEPS=[ 'catalog_helpers', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/db_raii', '$BUILD_DIR/mongo/db/namespace_string', '$BUILD_DIR/mongo/db/repl/repl_coordinator_interface', @@ -142,6 +143,7 @@ env.CppUnitTest( LIBDEPS=[ 'database', 'index_create', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/concurrency/lock_manager', '$BUILD_DIR/mongo/db/db_raii', '$BUILD_DIR/mongo/db/namespace_string', @@ -224,7 +226,7 @@ env.Library( 'database_holder_mock.cpp', ], LIBDEPS=[ - 'database_holder', + 'database_holder', '$BUILD_DIR/mongo/base', ], ) @@ -288,6 +290,7 @@ env.Library( ], LIBDEPS_PRIVATE=[ '$BUILD_DIR/mongo/db/logical_clock', + '$BUILD_DIR/mongo/db/repl/repl_settings', '$BUILD_DIR/mongo/db/storage/mmap_v1/mmap_v1_options', '$BUILD_DIR/mongo/db/storage/storage_engine_common', ], @@ -326,6 +329,7 @@ env.CppUnitTest( ], LIBDEPS=[ 'catalog_helpers', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/db_raii', '$BUILD_DIR/mongo/db/namespace_string', '$BUILD_DIR/mongo/db/repl/repl_coordinator_interface', @@ -343,6 +347,7 @@ env.CppUnitTest( LIBDEPS=[ 'catalog_helpers', 'uuid_catalog', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/db_raii', '$BUILD_DIR/mongo/db/namespace_string', '$BUILD_DIR/mongo/db/repl/repl_coordinator_interface', @@ -360,6 +365,7 @@ env.CppUnitTest( LIBDEPS=[ 'catalog_helpers', 'index_create', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/db_raii', '$BUILD_DIR/mongo/db/namespace_string', '$BUILD_DIR/mongo/db/repl/drop_pending_collection_reaper', @@ -378,6 +384,7 @@ env.CppUnitTest( ], LIBDEPS=[ 'catalog_helpers', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/db_raii', '$BUILD_DIR/mongo/db/namespace_string', '$BUILD_DIR/mongo/db/repl/drop_pending_collection_reaper', diff --git a/src/mongo/db/commands/SConscript b/src/mongo/db/commands/SConscript index b95489f622c..ba821f8ed68 100644 --- a/src/mongo/db/commands/SConscript +++ b/src/mongo/db/commands/SConscript @@ -36,11 +36,14 @@ env.Library( '$BUILD_DIR/mongo/db/commands', ], LIBDEPS_PRIVATE=[ - '$BUILD_DIR/mongo/db/auth/authcore', + '$BUILD_DIR/mongo/db/auth/auth', + '$BUILD_DIR/mongo/db/auth/authprivilege', + '$BUILD_DIR/mongo/db/server_options_core', '$BUILD_DIR/mongo/db/stats/counters', '$BUILD_DIR/mongo/transport/message_compressor', '$BUILD_DIR/mongo/transport/service_executor', '$BUILD_DIR/mongo/util/net/network', + '$BUILD_DIR/mongo/util/net/ssl_manager', '$BUILD_DIR/mongo/util/processinfo', 'server_status_core', ], @@ -52,7 +55,7 @@ env.Library( "write_commands/write_commands_common.cpp", ], LIBDEPS=[ - '$BUILD_DIR/mongo/db/auth/authcore', + '$BUILD_DIR/mongo/db/auth/auth', '$BUILD_DIR/mongo/base', '$BUILD_DIR/mongo/db/auth/authprivilege', '$BUILD_DIR/mongo/db/commands', @@ -86,12 +89,14 @@ env.Library( "rename_collection_common.cpp", ], LIBDEPS_PRIVATE=[ - '$BUILD_DIR/mongo/db/auth/authcore', + '$BUILD_DIR/mongo/bson/mutable/mutable_bson', + '$BUILD_DIR/mongo/db/auth/auth', '$BUILD_DIR/mongo/db/auth/authprivilege', '$BUILD_DIR/mongo/db/commands', '$BUILD_DIR/mongo/db/commands/test_commands_enabled', '$BUILD_DIR/mongo/db/common', '$BUILD_DIR/mongo/db/log_process_details', + '$BUILD_DIR/mongo/db/mongohasher', '$BUILD_DIR/mongo/logger/parse_log_component_settings', ], ) @@ -120,6 +125,7 @@ env.Library( ], LIBDEPS_PRIVATE=[ '$BUILD_DIR/mongo/db/audit', + '$BUILD_DIR/mongo/db/auth/sasl_options', '$BUILD_DIR/mongo/db/commands', '$BUILD_DIR/mongo/db/commands/test_commands_enabled', '$BUILD_DIR/mongo/db/common', @@ -285,7 +291,8 @@ env.Library( LIBDEPS_PRIVATE=[ '$BUILD_DIR/mongo/base', '$BUILD_DIR/mongo/client/clientdriver', - '$BUILD_DIR/mongo/db/auth/authmongod', + '$BUILD_DIR/mongo/db/auth/auth', + '$BUILD_DIR/mongo/db/auth/authprivilege', '$BUILD_DIR/mongo/db/background', '$BUILD_DIR/mongo/db/catalog/catalog_helpers', '$BUILD_DIR/mongo/db/catalog/catalog_impl', @@ -319,6 +326,7 @@ env.Library( 'kill_op_cmd_base.cpp' ], LIBDEPS=[ + '$BUILD_DIR/mongo/db/auth/authprivilege', '$BUILD_DIR/mongo/db/audit', '$BUILD_DIR/mongo/db/commands', '$BUILD_DIR/mongo/db/auth/authorization_manager_global', @@ -350,7 +358,9 @@ env.Library( '$BUILD_DIR/mongo/db/commands', ], LIBDEPS_PRIVATE=[ - '$BUILD_DIR/mongo/db/auth/authcore', + '$BUILD_DIR/mongo/db/auth/auth', + '$BUILD_DIR/mongo/db/auth/authprivilege', + '$BUILD_DIR/mongo/db/server_options_core', ], ) @@ -380,6 +390,7 @@ env.CppUnitTest( "index_filter_commands_test.cpp", ], LIBDEPS=[ + "$BUILD_DIR/mongo/db/auth/authmocks", "$BUILD_DIR/mongo/db/query/query_planner", "$BUILD_DIR/mongo/db/query/query_test_service_context", "$BUILD_DIR/mongo/db/serveronly", @@ -394,6 +405,7 @@ env.CppUnitTest( "mr_test.cpp", ], LIBDEPS=[ + "$BUILD_DIR/mongo/db/auth/authmocks", "$BUILD_DIR/mongo/db/repl/replmocks", "$BUILD_DIR/mongo/db/repl/storage_interface_impl", "$BUILD_DIR/mongo/db/serveronly", @@ -410,6 +422,7 @@ env.CppUnitTest( "plan_cache_commands_test.cpp", ], LIBDEPS=[ + "$BUILD_DIR/mongo/db/auth/authmocks", "$BUILD_DIR/mongo/db/query/query_planner", "$BUILD_DIR/mongo/db/query/query_test_service_context", "$BUILD_DIR/mongo/db/serveronly", diff --git a/src/mongo/db/exec/SConscript b/src/mongo/db/exec/SConscript index c0c075d8cef..a5ffcde4cbb 100644 --- a/src/mongo/db/exec/SConscript +++ b/src/mongo/db/exec/SConscript @@ -57,6 +57,7 @@ env.CppUnitTest( "queued_data_stage_test.cpp", ], LIBDEPS = [ + "$BUILD_DIR/mongo/db/auth/authmocks", "$BUILD_DIR/mongo/db/query_exec", "$BUILD_DIR/mongo/db/serveronly", "$BUILD_DIR/mongo/db/service_context_d", @@ -71,6 +72,7 @@ env.CppUnitTest( "sort_test.cpp", ], LIBDEPS = [ + "$BUILD_DIR/mongo/db/auth/authmocks", "$BUILD_DIR/mongo/db/query_exec", "$BUILD_DIR/mongo/db/serveronly", "$BUILD_DIR/mongo/db/service_context_d", @@ -87,6 +89,7 @@ env.CppUnitTest( "projection_exec_test.cpp", ], LIBDEPS = [ + "$BUILD_DIR/mongo/db/auth/authmocks", "$BUILD_DIR/mongo/db/query_exec", "$BUILD_DIR/mongo/db/query/collation/collator_interface_mock", "$BUILD_DIR/mongo/db/query/query_test_service_context", diff --git a/src/mongo/db/free_mon/SConscript b/src/mongo/db/free_mon/SConscript index ac990e7e8aa..10f73e3f399 100644 --- a/src/mongo/db/free_mon/SConscript +++ b/src/mongo/db/free_mon/SConscript @@ -63,7 +63,7 @@ else: 'free_mon', '$BUILD_DIR/mongo/base', '$BUILD_DIR/mongo/db/commands', - '$BUILD_DIR/mongo/db/auth/authcore', + '$BUILD_DIR/mongo/db/auth/auth', '$BUILD_DIR/mongo/db/auth/authprivilege', ], ) @@ -77,6 +77,7 @@ fmEnv.CppUnitTest( 'free_mon_storage_test.cpp', ], LIBDEPS=[ + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/repl/replmocks', '$BUILD_DIR/mongo/db/repl/storage_interface_impl', '$BUILD_DIR/mongo/db/serveronly', diff --git a/src/mongo/db/ftdc/SConscript b/src/mongo/db/ftdc/SConscript index fd84a46cdb3..4822907a540 100644 --- a/src/mongo/db/ftdc/SConscript +++ b/src/mongo/db/ftdc/SConscript @@ -69,7 +69,8 @@ env.Library( 'ftdc_server' ], LIBDEPS_PRIVATE=[ - '$BUILD_DIR/mongo/db/auth/authcore', + '$BUILD_DIR/mongo/db/auth/auth', + '$BUILD_DIR/mongo/db/auth/authprivilege', ], ) diff --git a/src/mongo/db/logical_session_cache_test.cpp b/src/mongo/db/logical_session_cache_test.cpp index 3d1b625aff1..10dcc9d8e4a 100644 --- a/src/mongo/db/logical_session_cache_test.cpp +++ b/src/mongo/db/logical_session_cache_test.cpp @@ -28,15 +28,15 @@ #include "mongo/platform/basic.h" +#include "mongo/db/logical_session_cache_impl.h" + +#include "mongo/bson/oid.h" #include "mongo/db/auth/authorization_manager.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/bson/oid.h" #include "mongo/db/auth/user_name.h" #include "mongo/db/logical_session_cache.h" -#include "mongo/db/logical_session_cache_impl.h" #include "mongo/db/logical_session_id.h" #include "mongo/db/logical_session_id_helpers.h" #include "mongo/db/operation_context_noop.h" @@ -69,11 +69,7 @@ public: _sessions(std::make_shared<MockSessionsCollectionImpl>()) {} void setUp() override { - auto localManagerState = stdx::make_unique<AuthzManagerExternalStateMock>(); - localManagerState.get()->setAuthzVersion(AuthorizationManager::schemaVersion28SCRAM); - auto uniqueAuthzManager = - stdx::make_unique<AuthorizationManager>(std::move(localManagerState)); - AuthorizationManager::set(&serviceContext, std::move(uniqueAuthzManager)); + AuthorizationManager::set(&serviceContext, AuthorizationManager::create()); auto client = serviceContext.makeClient("testClient"); _opCtx = client->makeOperationContext(); diff --git a/src/mongo/db/logical_session_id_test.cpp b/src/mongo/db/logical_session_id_test.cpp index 6a62b9a972c..3d1d1e0563e 100644 --- a/src/mongo/db/logical_session_id_test.cpp +++ b/src/mongo/db/logical_session_id_test.cpp @@ -38,6 +38,7 @@ #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_manager_impl.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" @@ -76,31 +77,33 @@ public: session = transportLayer.createSession(); client = serviceContext.makeClient("testClient", session); RestrictionEnvironment::set( - session, stdx::make_unique<RestrictionEnvironment>(SockAddr(), SockAddr())); + session, std::make_unique<RestrictionEnvironment>(SockAddr(), SockAddr())); _opCtx = client->makeOperationContext(); - auto localManagerState = stdx::make_unique<AuthzManagerExternalStateMock>(); + auto localManagerState = std::make_unique<AuthzManagerExternalStateMock>(); managerState = localManagerState.get(); managerState->setAuthzVersion(AuthorizationManager::schemaVersion26Final); - auto uniqueAuthzManager = - stdx::make_unique<AuthorizationManager>(std::move(localManagerState)); + auto uniqueAuthzManager = std::make_unique<AuthorizationManagerImpl>( + std::move(localManagerState), + AuthorizationManagerImpl::InstallMockForTestingOrAuthImpl{}); authzManager = uniqueAuthzManager.get(); AuthorizationManager::set(&serviceContext, std::move(uniqueAuthzManager)); - auto localSessionState = stdx::make_unique<AuthzSessionExternalStateMock>(authzManager); + auto localSessionState = std::make_unique<AuthzSessionExternalStateMock>(authzManager); sessionState = localSessionState.get(); - auto localauthzSession = - stdx::make_unique<AuthorizationSessionForTest>(std::move(localSessionState)); + auto localauthzSession = std::make_unique<AuthorizationSessionForTest>( + std::move(localSessionState), + AuthorizationSessionImpl::InstallMockForTestingOrAuthImpl{}); authzSession = localauthzSession.get(); AuthorizationSession::set(client.get(), std::move(localauthzSession)); authzManager->setAuthEnabled(true); auto localServiceLiason = - stdx::make_unique<MockServiceLiason>(std::make_shared<MockServiceLiasonImpl>()); + std::make_unique<MockServiceLiason>(std::make_shared<MockServiceLiasonImpl>()); auto localSessionsCollection = stdx::make_unique<MockSessionsCollection>( std::make_shared<MockSessionsCollectionImpl>()); - auto localLogicalSessionCache = stdx::make_unique<LogicalSessionCacheImpl>( + auto localLogicalSessionCache = std::make_unique<LogicalSessionCacheImpl>( std::move(localServiceLiason), std::move(localSessionsCollection), nullptr); LogicalSessionCache::set(&serviceContext, std::move(localLogicalSessionCache)); diff --git a/src/mongo/db/ops/SConscript b/src/mongo/db/ops/SConscript index 1e077f4f4ab..f26866db08b 100644 --- a/src/mongo/db/ops/SConscript +++ b/src/mongo/db/ops/SConscript @@ -67,6 +67,7 @@ env.CppUnitTest( target='write_ops_retryability_test', source='write_ops_retryability_test.cpp', LIBDEPS=[ + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/query_exec', '$BUILD_DIR/mongo/db/repl/mock_repl_coord_server_fixture', '$BUILD_DIR/mongo/db/repl/oplog_entry', diff --git a/src/mongo/db/pipeline/SConscript b/src/mongo/db/pipeline/SConscript index 02f5c3a0520..ff3eebda9bb 100644 --- a/src/mongo/db/pipeline/SConscript +++ b/src/mongo/db/pipeline/SConscript @@ -224,7 +224,7 @@ env.CppUnitTest( 'sequential_document_cache_test.cpp', ], LIBDEPS=[ - '$BUILD_DIR/mongo/db/auth/authorization_manager_mock_init', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/repl/oplog_entry', '$BUILD_DIR/mongo/db/repl/replmocks', '$BUILD_DIR/mongo/db/service_context', @@ -241,7 +241,7 @@ env.CppUnitTest( target='document_source_facet_test', source='document_source_facet_test.cpp', LIBDEPS=[ - '$BUILD_DIR/mongo/db/auth/authorization_manager_mock_init', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/service_context_noop_init', '$BUILD_DIR/mongo/s/is_mongos', 'pipeline', @@ -351,7 +351,7 @@ env.CppUnitTest( target='tee_buffer_test', source='tee_buffer_test.cpp', LIBDEPS=[ - '$BUILD_DIR/mongo/db/auth/authorization_manager_mock_init', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/service_context_noop_init', '$BUILD_DIR/mongo/s/is_mongos', 'document_source_mock', @@ -392,7 +392,7 @@ env.CppUnitTest( 'pipeline_test.cpp', ], LIBDEPS=[ - '$BUILD_DIR/mongo/db/auth/authorization_manager_mock_init', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/query/collation/collator_interface_mock', '$BUILD_DIR/mongo/db/repl/replmocks', '$BUILD_DIR/mongo/db/service_context', @@ -503,7 +503,7 @@ env.CppUnitTest( source='resume_token_test.cpp', LIBDEPS=[ '$BUILD_DIR/mongo/db/service_context_noop_init', - '$BUILD_DIR/mongo/db/auth/authorization_manager_mock_init', + '$BUILD_DIR/mongo/db/auth/authmocks', 'document_sources_idl', ], ) diff --git a/src/mongo/db/query/SConscript b/src/mongo/db/query/SConscript index 3638d5ba00b..37f68acfc2d 100644 --- a/src/mongo/db/query/SConscript +++ b/src/mongo/db/query/SConscript @@ -116,6 +116,7 @@ env.CppUnitTest( ], LIBDEPS=[ "query_test_service_context", + "$BUILD_DIR/mongo/db/auth/authmocks", "$BUILD_DIR/mongo/db/query_exec", "$BUILD_DIR/mongo/db/serveronly", "$BUILD_DIR/mongo/db/service_context_d", @@ -398,6 +399,7 @@ env.CppUnitTest( ], LIBDEPS=[ "query_planner_test_fixture", + "$BUILD_DIR/mongo/db/auth/authmocks", "$BUILD_DIR/mongo/db/query_exec", "$BUILD_DIR/mongo/db/serveronly", "$BUILD_DIR/mongo/db/service_context_d", diff --git a/src/mongo/db/repl/SConscript b/src/mongo/db/repl/SConscript index 01039bcf869..6b752b1d013 100644 --- a/src/mongo/db/repl/SConscript +++ b/src/mongo/db/repl/SConscript @@ -15,6 +15,7 @@ env.Library( LIBDEPS_PRIVATE=[ 'dbcheck', 'repl_coordinator_interface', + 'repl_settings', '$BUILD_DIR/mongo/base', '$BUILD_DIR/mongo/db/background', '$BUILD_DIR/mongo/db/catalog/catalog_helpers', @@ -52,6 +53,7 @@ env.CppUnitTest( 'oplog_entry', 'replmocks', 'storage_interface_impl', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/service_context_d_test_fixture', '$BUILD_DIR/mongo/rpc/command_status', ], @@ -63,6 +65,7 @@ env.CppUnitTest( 'oplog_test.cpp', ], LIBDEPS=[ + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/service_context_d_test_fixture', '$BUILD_DIR/mongo/unittest/concurrency', '$BUILD_DIR/mongo/util/concurrency/thread_pool', @@ -83,6 +86,7 @@ env.CppUnitTest( 'oplog_entry', 'replmocks', 'storage_interface_impl', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/service_context_d_test_fixture', '$BUILD_DIR/mongo/rpc/command_status', ], @@ -263,6 +267,7 @@ env.CppUnitTest( 'oplog_interface_local', 'replmocks', 'storage_interface_impl', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/service_context_d_test_fixture', ], ) @@ -301,6 +306,7 @@ env.CppUnitTest( 'replication_consistency_markers_impl', 'replmocks', 'storage_interface_impl', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/service_context_d_test_fixture', ], ) @@ -328,6 +334,7 @@ env.CppUnitTest( 'replmocks', 'replication_recovery', 'storage_interface_impl', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/service_context_d_test_fixture', ], ) @@ -356,6 +363,7 @@ env.CppUnitTest( 'replication_process', 'replmocks', 'storage_interface_impl', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/service_context_d_test_fixture', ], ) @@ -383,6 +391,7 @@ env.CppUnitTest( 'drop_pending_collection_reaper', 'replmocks', 'storage_interface_impl', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/service_context_d_test_fixture', ], ) @@ -442,6 +451,7 @@ env.CppUnitTest( 'oplog_interface_local', 'replmocks', 'storage_interface_impl', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/service_context_d_test_fixture', '$BUILD_DIR/mongo/unittest/concurrency', ], @@ -520,7 +530,7 @@ env.Library( LIBDEPS=[ '$BUILD_DIR/mongo/base', '$BUILD_DIR/mongo/client/clientdriver', - '$BUILD_DIR/mongo/db/auth/authcore', + '$BUILD_DIR/mongo/db/auth/auth', '$BUILD_DIR/mongo/db/auth/authorization_manager_global', '$BUILD_DIR/mongo/util/net/network', ], @@ -561,6 +571,7 @@ env.CppUnitTest( 'rs_rollback_test.cpp', ], LIBDEPS=[ + '$BUILD_DIR/mongo/db/auth/authmocks', 'oplog_interface_local', 'rollback_test_fixture', 'rs_rollback', @@ -598,6 +609,7 @@ env.CppUnitTest( 'rollback_impl', 'rollback_test_fixture', '$BUILD_DIR/mongo/base', + '$BUILD_DIR/mongo/db/auth/authmocks', ], ) @@ -640,6 +652,7 @@ env.Library( 'oplog_entry', 'oplogreader', 'repl_coordinator_interface', + 'repl_settings', 'storage_interface', ], LIBDEPS_PRIVATE=[ @@ -653,7 +666,8 @@ env.Library( 'idempotency_test_fixture.cpp', ], LIBDEPS=[ - 'sync_tail_test_fixture' + 'sync_tail_test_fixture', + '$BUILD_DIR/mongo/db/auth/authmocks', ], ) @@ -678,6 +692,7 @@ env.CppUnitTest( 'sync_tail_test.cpp', ], LIBDEPS=[ + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/dbdirectclient', '$BUILD_DIR/mongo/db/commands/mongod_fcv', 'idempotency_test_fixture', @@ -759,8 +774,11 @@ env.CppUnitTest( LIBDEPS=[ 'topology_coordinator', 'replica_set_messages', - '$BUILD_DIR/mongo/db/auth/authorization_manager_mock_init', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/service_context_noop_init', + '$BUILD_DIR/mongo/executor/network_interface_factory', + '$BUILD_DIR/mongo/executor/network_interface_mock', + '$BUILD_DIR/mongo/executor/network_interface_thread_pool', ], ) @@ -816,10 +834,13 @@ env.Library( 'service_context_repl_mock_init', 'topology_coordinator', '$BUILD_DIR/mongo/db/auth/authorization_manager_global', - '$BUILD_DIR/mongo/db/auth/authorization_manager_mock_init', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/executor/network_interface_mock', + '$BUILD_DIR/mongo/executor/network_interface_factory', + '$BUILD_DIR/mongo/executor/network_interface_thread_pool', '$BUILD_DIR/mongo/executor/thread_pool_task_executor', '$BUILD_DIR/mongo/unittest/unittest', + ], ) @@ -842,7 +863,7 @@ env.CppUnitTest( LIBDEPS=[ 'repl_coordinator_impl', 'replmocks', - '$BUILD_DIR/mongo/db/auth/authorization_manager_mock_init', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/service_context_noop_init', ], ) @@ -867,7 +888,7 @@ env.CppUnitTest( 'repl_coordinator_impl', 'replmocks', '$BUILD_DIR/mongo/db/service_context_noop_init', - '$BUILD_DIR/mongo/db/auth/authorization_manager_mock_init', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/executor/thread_pool_task_executor_test_fixture', ], ) @@ -882,7 +903,7 @@ env.CppUnitTest( 'replica_set_messages', 'replmocks', '$BUILD_DIR/mongo/db/service_context_noop_init', - '$BUILD_DIR/mongo/db/auth/authorization_manager_mock_init', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/executor/thread_pool_task_executor_test_fixture', ], ) @@ -896,7 +917,7 @@ env.CppUnitTest( 'repl_coordinator_impl', 'replica_set_messages', 'replmocks', - '$BUILD_DIR/mongo/db/auth/authorization_manager_mock_init', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/service_context_noop_init', ], ) @@ -912,7 +933,7 @@ env.CppUnitTest( 'replica_set_messages', 'replmocks', '$BUILD_DIR/mongo/db/service_context_noop_init', - '$BUILD_DIR/mongo/db/auth/authorization_manager_mock_init', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/executor/thread_pool_task_executor_test_fixture', ], ) @@ -1030,8 +1051,9 @@ env.CppUnitTest('isself_test', 'isself_test.cpp', ], LIBDEPS=[ + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/serveronly', - "$BUILD_DIR/mongo/db/service_context_d", + '$BUILD_DIR/mongo/db/service_context_d', 'isself', ], ) @@ -1044,7 +1066,8 @@ env.Library( ], LIBDEPS=[ '$BUILD_DIR/mongo/base', - '$BUILD_DIR/mongo/db/auth/authcore', + '$BUILD_DIR/mongo/db/auth/auth', + '$BUILD_DIR/mongo/db/auth/authprivilege', '$BUILD_DIR/mongo/db/commands', '$BUILD_DIR/mongo/db/lasterror', '$BUILD_DIR/mongo/db/repl/repl_coordinator_interface', @@ -1167,7 +1190,7 @@ env.CppUnitTest( source='reporter_test.cpp', LIBDEPS=[ 'reporter', - '$BUILD_DIR/mongo/db/auth/authorization_manager_mock_init', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/executor/thread_pool_task_executor_test_fixture', '$BUILD_DIR/mongo/db/service_context_noop_init', '$BUILD_DIR/mongo/unittest/task_executor_proxy', @@ -1252,7 +1275,7 @@ env.CppUnitTest( LIBDEPS=[ 'collection_cloner', 'base_cloner_test_fixture', - '$BUILD_DIR/mongo/db/auth/authorization_manager_mock_init', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/auth/authorization_manager_global', '$BUILD_DIR/mongo/db/service_context_noop_init', '$BUILD_DIR/mongo/unittest/task_executor_proxy', @@ -1279,7 +1302,7 @@ env.CppUnitTest( LIBDEPS=[ 'database_cloner', 'base_cloner_test_fixture', - '$BUILD_DIR/mongo/db/auth/authorization_manager_mock_init', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/commands/list_collections_filter', '$BUILD_DIR/mongo/db/service_context_noop_init', '$BUILD_DIR/mongo/unittest/task_executor_proxy', @@ -1304,7 +1327,7 @@ env.CppUnitTest( 'databases_cloner', 'oplog_entry', 'replmocks', - '$BUILD_DIR/mongo/db/auth/authorization_manager_mock_init', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/service_context_noop_init', '$BUILD_DIR/mongo/executor/thread_pool_task_executor_test_fixture', '$BUILD_DIR/mongo/unittest/task_executor_proxy', @@ -1317,7 +1340,7 @@ env.Library( 'task_runner.cpp', ], LIBDEPS=[ - '$BUILD_DIR/mongo/db/auth/authcore', + '$BUILD_DIR/mongo/db/auth/auth', '$BUILD_DIR/mongo/util/concurrency/thread_pool', ], ) @@ -1331,7 +1354,7 @@ env.Library( 'replmocks', 'service_context_repl_mock_init', 'task_runner', - '$BUILD_DIR/mongo/db/auth/authorization_manager_mock_init', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/auth/authorization_manager_global', '$BUILD_DIR/mongo/db/service_context', '$BUILD_DIR/mongo/unittest/concurrency', @@ -1397,7 +1420,7 @@ env.CppUnitTest( LIBDEPS=[ 'multiapplier', 'service_context_repl_mock_init', - '$BUILD_DIR/mongo/db/auth/authorization_manager_mock_init', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/executor/thread_pool_task_executor_test_fixture', '$BUILD_DIR/mongo/unittest/task_executor_proxy', ], @@ -1475,7 +1498,7 @@ env.CppUnitTest( 'initial_syncer_test.cpp', ], LIBDEPS=[ - '$BUILD_DIR/mongo/db/auth/authorization_manager_mock_init', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/commands/feature_compatibility_parsers', '$BUILD_DIR/mongo/db/query/command_request_response', '$BUILD_DIR/mongo/executor/thread_pool_task_executor_test_fixture', @@ -1543,13 +1566,14 @@ env.Library( LIBDEPS=[ '$BUILD_DIR/mongo/base', '$BUILD_DIR/mongo/client/clientdriver', - '$BUILD_DIR/mongo/db/auth/authcore', + '$BUILD_DIR/mongo/db/auth/auth', '$BUILD_DIR/mongo/db/auth/saslauth', '$BUILD_DIR/mongo/db/dbhelpers', '$BUILD_DIR/mongo/db/query_exec', 'oplog', 'oplogreader', 'repl_coordinator_interface', + 'repl_settings', 'replica_set_messages', ], LIBDEPS_PRIVATE=[ @@ -1567,7 +1591,7 @@ env.Library( LIBDEPS=[ '$BUILD_DIR/mongo/base', '$BUILD_DIR/mongo/client/clientdriver', - '$BUILD_DIR/mongo/db/auth/authcore', + '$BUILD_DIR/mongo/db/auth/auth', '$BUILD_DIR/mongo/db/catalog/catalog_helpers', '$BUILD_DIR/mongo/db/cloner', '$BUILD_DIR/mongo/db/concurrency/lock_manager', diff --git a/src/mongo/db/repl/topology_coordinator_v1_test.cpp b/src/mongo/db/repl/topology_coordinator_v1_test.cpp index 43ee132afa9..1cdff1562fe 100644 --- a/src/mongo/db/repl/topology_coordinator_v1_test.cpp +++ b/src/mongo/db/repl/topology_coordinator_v1_test.cpp @@ -74,16 +74,16 @@ public: virtual void setUp() { _options = TopologyCoordinator::Options{}; _options.maxSyncSourceLagSecs = Seconds{100}; - _topo.reset(new TopologyCoordinator(_options)); + _topo = std::make_unique<TopologyCoordinator>(_options); _now = Date_t(); _selfIndex = -1; - _cbData.reset(new executor::TaskExecutor::CallbackArgs( - NULL, executor::TaskExecutor::CallbackHandle(), Status::OK())); + _cbData = std::make_unique<executor::TaskExecutor::CallbackArgs>( + nullptr, executor::TaskExecutor::CallbackHandle(), Status::OK()); } virtual void tearDown() { - _topo.reset(NULL); - _cbData.reset(NULL); + _topo = nullptr; + _cbData = nullptr; } protected: diff --git a/src/mongo/db/s/SConscript b/src/mongo/db/s/SConscript index 0c3d7918cb6..09c4b590cca 100644 --- a/src/mongo/db/s/SConscript +++ b/src/mongo/db/s/SConscript @@ -74,6 +74,7 @@ env.CppUnitTest( ], LIBDEPS=[ 'sharding_runtime_d', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/s/config_server_test_fixture', ] ) @@ -173,6 +174,7 @@ env.CppUnitTest( 'balancer/type_migration_test.cpp', ], LIBDEPS=[ + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/s/config_server_test_fixture', '$BUILD_DIR/mongo/util/version_impl', 'balancer', @@ -297,6 +299,7 @@ env.CppUnitTest( ], LIBDEPS=[ 'sharding_runtime_d', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/query/query_request', '$BUILD_DIR/mongo/s/catalog/dist_lock_manager_mock', '$BUILD_DIR/mongo/s/catalog/sharding_catalog_client_impl', @@ -315,6 +318,7 @@ env.CppUnitTest( ], LIBDEPS=[ '$BUILD_DIR/mongo/client/remote_command_targeter_mock', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/repl/replmocks', '$BUILD_DIR/mongo/db/serveronly', '$BUILD_DIR/mongo/executor/network_test_env', @@ -329,6 +333,7 @@ env.CppUnitTest( 'shard_metadata_util_test.cpp', ], LIBDEPS=[ + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/s/shard_server_test_fixture', 'sharding', ], @@ -340,6 +345,7 @@ env.CppUnitTest( 'split_vector_test.cpp', ], LIBDEPS=[ + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/dbdirectclient', '$BUILD_DIR/mongo/s/shard_server_test_fixture', 'sharding', @@ -352,6 +358,7 @@ env.CppUnitTest( 'session_catalog_migration_source_test.cpp', ], LIBDEPS=[ + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/repl/mock_repl_coord_server_fixture', 'sharding', ] @@ -363,6 +370,7 @@ env.CppUnitTest( 'session_catalog_migration_destination_test.cpp', ], LIBDEPS=[ + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/ops/write_ops_exec', '$BUILD_DIR/mongo/s/catalog/sharding_catalog_client_mock', '$BUILD_DIR/mongo/s/shard_server_test_fixture', @@ -389,6 +397,7 @@ env.CppUnitTest( 'config/sharding_catalog_manager_split_chunk_test.cpp', ], LIBDEPS=[ + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/s/config_server_test_fixture', '$BUILD_DIR/mongo/util/version_impl', ] diff --git a/src/mongo/db/storage/ephemeral_for_test/SConscript b/src/mongo/db/storage/ephemeral_for_test/SConscript index 13e16746684..0b1c846d3a6 100644 --- a/src/mongo/db/storage/ephemeral_for_test/SConscript +++ b/src/mongo/db/storage/ephemeral_for_test/SConscript @@ -79,7 +79,7 @@ env.CppUnitTest( 'storage_ephemeral_for_test_core', ], LIBDEPS_PRIVATE=[ - '$BUILD_DIR/mongo/db/auth/authorization_manager_mock_init', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/repl/service_context_repl_mock_init', ], ) diff --git a/src/mongo/db/storage/kv/SConscript b/src/mongo/db/storage/kv/SConscript index 1735a8c010a..d8e8f7665f7 100644 --- a/src/mongo/db/storage/kv/SConscript +++ b/src/mongo/db/storage/kv/SConscript @@ -118,7 +118,7 @@ env.CppUnitTest( '$BUILD_DIR/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_record_store', ], LIBDEPS_PRIVATE=[ - '$BUILD_DIR/mongo/db/auth/authorization_manager_mock_init' + '$BUILD_DIR/mongo/db/auth/authmocks' ], ) @@ -136,7 +136,7 @@ env.CppUnitTest( '$BUILD_DIR/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_record_store', ], LIBDEPS_PRIVATE=[ - '$BUILD_DIR/mongo/db/auth/authorization_manager_mock_init' + '$BUILD_DIR/mongo/db/auth/authmocks' ], ) @@ -146,6 +146,7 @@ env.CppUnitTest( 'kv_storage_engine_test.cpp', ], LIBDEPS=[ + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/catalog/collection_options', '$BUILD_DIR/mongo/db/namespace_string', '$BUILD_DIR/mongo/db/repair_database', diff --git a/src/mongo/db/storage/mmap_v1/SConscript b/src/mongo/db/storage/mmap_v1/SConscript index c879be79a24..857bcfac99f 100644 --- a/src/mongo/db/storage/mmap_v1/SConscript +++ b/src/mongo/db/storage/mmap_v1/SConscript @@ -220,6 +220,7 @@ if mmapv1: source=['mmap_v1_init_test.cpp', ], LIBDEPS=[ + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/serveronly', '$BUILD_DIR/mongo/db/service_context', '$BUILD_DIR/mongo/db/service_context_d', diff --git a/src/mongo/db/storage/mobile/SConscript b/src/mongo/db/storage/mobile/SConscript index 1f481c23d0a..5ff2276f1c0 100644 --- a/src/mongo/db/storage/mobile/SConscript +++ b/src/mongo/db/storage/mobile/SConscript @@ -91,7 +91,7 @@ env.CppUnitTest( 'storage_mobile_core', ], LIBDEPS_PRIVATE=[ - '$BUILD_DIR/mongo/db/auth/authorization_manager_mock_init', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/repl/service_context_repl_mock_init', ], ) diff --git a/src/mongo/db/storage/wiredtiger/SConscript b/src/mongo/db/storage/wiredtiger/SConscript index 142a108aaa7..25b49ee34fe 100644 --- a/src/mongo/db/storage/wiredtiger/SConscript +++ b/src/mongo/db/storage/wiredtiger/SConscript @@ -120,6 +120,7 @@ if wiredtiger: source=['wiredtiger_init_test.cpp', ], LIBDEPS=[ + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/serveronly', '$BUILD_DIR/mongo/db/service_context', '$BUILD_DIR/mongo/db/service_context_d', @@ -168,7 +169,7 @@ if wiredtiger: ) # All of these tests fail to compile under undefined behavior - # sanitizer due to unexpressed circular dependency edges. In particualr + # sanitizer due to unexpressed circular dependency edges. In particular # they all need a definition from the 'catalog'. if not using_ubsan: wtEnv.CppUnitTest( @@ -217,7 +218,7 @@ if wiredtiger: 'wiredtiger_kv_engine_test.cpp', ], LIBDEPS=[ - '$BUILD_DIR/mongo/db/auth/authorization_manager_mock_init', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/storage/kv/kv_engine_test_harness', 'storage_wiredtiger_mock', ], diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_recovery_unit_test.cpp b/src/mongo/db/storage/wiredtiger/wiredtiger_recovery_unit_test.cpp index 62b9e188c34..bd910986ad5 100644 --- a/src/mongo/db/storage/wiredtiger/wiredtiger_recovery_unit_test.cpp +++ b/src/mongo/db/storage/wiredtiger/wiredtiger_recovery_unit_test.cpp @@ -27,6 +27,7 @@ */ #include "mongo/db/storage/wiredtiger/wiredtiger_recovery_unit.h" + #include "mongo/base/checked_cast.h" #include "mongo/db/storage/recovery_unit_test_harness.h" #include "mongo/db/storage/wiredtiger/wiredtiger_kv_engine.h" @@ -103,7 +104,7 @@ private: }; std::unique_ptr<HarnessHelper> makeHarnessHelper() { - return stdx::make_unique<WiredTigerRecoveryUnitHarnessHelper>(); + return std::make_unique<WiredTigerRecoveryUnitHarnessHelper>(); } MONGO_INITIALIZER(RegisterHarnessFactory)(InitializerContext* const) { diff --git a/src/mongo/db/views/SConscript b/src/mongo/db/views/SConscript index 01e99381b7e..3eed93ef638 100644 --- a/src/mongo/db/views/SConscript +++ b/src/mongo/db/views/SConscript @@ -44,7 +44,7 @@ env.CppUnitTest( ], LIBDEPS=[ 'views', - '$BUILD_DIR/mongo/db/auth/authorization_manager_mock_init', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/query/collation/collator_interface_mock', '$BUILD_DIR/mongo/db/query/query_test_service_context', '$BUILD_DIR/mongo/db/service_context_noop_init', diff --git a/src/mongo/dbtests/SConscript b/src/mongo/dbtests/SConscript index 2bc24a869ad..05515cbca53 100644 --- a/src/mongo/dbtests/SConscript +++ b/src/mongo/dbtests/SConscript @@ -118,7 +118,7 @@ dbtest = env.Program( ], LIBDEPS=[ "$BUILD_DIR/mongo/bson/mutable/mutable_bson_test_utils", - "$BUILD_DIR/mongo/db/auth/authmocks", + "$BUILD_DIR/mongo/db/auth/authmongod", "$BUILD_DIR/mongo/db/bson/dotted_path_support", "$BUILD_DIR/mongo/db/commands/mongod", "$BUILD_DIR/mongo/db/commands/test_commands_enabled", diff --git a/src/mongo/executor/SConscript b/src/mongo/executor/SConscript index 4b75702604e..6a6e061bf12 100644 --- a/src/mongo/executor/SConscript +++ b/src/mongo/executor/SConscript @@ -144,6 +144,7 @@ env.Library(target='network_test_env', source=['network_test_env.cpp',], LIBDEPS=[ '$BUILD_DIR/mongo/db/commands', + '$BUILD_DIR/mongo/db/query/command_request_response', 'network_interface_mock', 'task_executor_interface', ]) @@ -286,7 +287,7 @@ env.CppIntegrationTest( '$BUILD_DIR/mongo/executor/thread_pool_task_executor', '$BUILD_DIR/mongo/executor/network_interface_thread_pool', '$BUILD_DIR/mongo/executor/network_interface_factory', - '$BUILD_DIR/mongo/db/auth/authorization_manager_mock_init', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/service_context_noop_init', '$BUILD_DIR/mongo/rpc/command_status', '$BUILD_DIR/mongo/util/version_impl', diff --git a/src/mongo/rpc/SConscript b/src/mongo/rpc/SConscript index 637c62ffaa7..b588608bdee 100644 --- a/src/mongo/rpc/SConscript +++ b/src/mongo/rpc/SConscript @@ -132,13 +132,12 @@ env.Library( ], ) -env.Clone().InjectModule("enterprise").Library( +env.Library( target=[ 'metadata', ], source=[ 'metadata.cpp', - 'metadata/audit_metadata.cpp', 'metadata/config_server_metadata.cpp', 'metadata/egress_metadata_hook_list.cpp', 'metadata/logical_time_metadata.cpp', @@ -149,6 +148,7 @@ env.Clone().InjectModule("enterprise").Library( ], LIBDEPS=[ 'client_metadata', + 'audit_metadata', '$BUILD_DIR/mongo/base', '$BUILD_DIR/mongo/bson/util/bson_extract', '$BUILD_DIR/mongo/client/read_preference', @@ -158,6 +158,23 @@ env.Clone().InjectModule("enterprise").Library( ], ) +env.Clone().InjectModule("enterprise").Library( + target=[ + 'audit_metadata', + ], + source=[ + 'metadata/audit_metadata.cpp', + ], + LIBDEPS_PRIVATE=[ + '$BUILD_DIR/mongo/bson/util/bson_extract', + '$BUILD_DIR/mongo/db/auth/auth_rolename', + '$BUILD_DIR/mongo/db/auth/user_name', + ], + LIBDEPS=[ + '$BUILD_DIR/mongo/base', + ], +) + env.CppUnitTest( target=[ 'rpc_metadata_test', diff --git a/src/mongo/s/SConscript b/src/mongo/s/SConscript index 5f0f3d9e934..c884eef62ef 100644 --- a/src/mongo/s/SConscript +++ b/src/mongo/s/SConscript @@ -364,6 +364,7 @@ env.CppUnitTest( 'shard_key_pattern_test.cpp', ], LIBDEPS=[ + "$BUILD_DIR/mongo/db/auth/authmocks", '$BUILD_DIR/mongo/db/s/sharding', '$BUILD_DIR/mongo/db/serveronly', 'catalog_cache_test_fixture', diff --git a/src/mongo/s/catalog/SConscript b/src/mongo/s/catalog/SConscript index e0b1f9b936a..88d3a3eb090 100644 --- a/src/mongo/s/catalog/SConscript +++ b/src/mongo/s/catalog/SConscript @@ -126,6 +126,7 @@ env.CppUnitTest( 'replset_dist_lock_manager_test.cpp', ], LIBDEPS=[ + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/s/catalog/dist_lock_catalog_mock', '$BUILD_DIR/mongo/s/catalog/sharding_catalog_client_mock', '$BUILD_DIR/mongo/s/shard_server_test_fixture', diff --git a/src/mongo/s/client/SConscript b/src/mongo/s/client/SConscript index 69a9389e784..a8079187d31 100644 --- a/src/mongo/s/client/SConscript +++ b/src/mongo/s/client/SConscript @@ -98,6 +98,7 @@ env.CppUnitTest( 'shard_local_test.cpp', ], LIBDEPS=[ + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/repl/replmocks', '$BUILD_DIR/mongo/db/service_context_d_test_fixture', 'shard_local', diff --git a/src/mongo/s/commands/SConscript b/src/mongo/s/commands/SConscript index ca9d7596330..88dc1fbb3ed 100644 --- a/src/mongo/s/commands/SConscript +++ b/src/mongo/s/commands/SConscript @@ -95,7 +95,8 @@ env.Library( env.Idlc('cluster_multicast.idl')[0], ], LIBDEPS_PRIVATE=[ - '$BUILD_DIR/mongo/db/auth/authmongos', + '$BUILD_DIR/mongo/db/auth/auth', + '$BUILD_DIR/mongo/db/auth/saslauth', '$BUILD_DIR/mongo/db/commands/core', '$BUILD_DIR/mongo/db/commands/current_op_common', '$BUILD_DIR/mongo/db/commands/servers', @@ -125,7 +126,7 @@ env.CppUnitTest( ], LIBDEPS=[ 'shared_cluster_commands', - '$BUILD_DIR/mongo/db/auth/authorization_manager_mock_init', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/service_context_noop_init', '$BUILD_DIR/mongo/db/logical_time', '$BUILD_DIR/mongo/unittest/unittest', @@ -140,7 +141,7 @@ env.CppUnitTest( LIBDEPS=[ 'shared_cluster_commands', '$BUILD_DIR/mongo/s/catalog_cache_test_fixture', - '$BUILD_DIR/mongo/db/auth/authorization_manager_mock_init', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/service_context_noop_init', '$BUILD_DIR/mongo/db/logical_clock', '$BUILD_DIR/mongo/unittest/unittest', @@ -154,7 +155,8 @@ env.CppUnitTest( ], LIBDEPS=[ 'cluster_commands', - '$BUILD_DIR/mongo/db/auth/authorization_manager_mock_init', + '$BUILD_DIR/mongo/db/auth/authmocks', + '$BUILD_DIR/mongo/db/auth/saslauth', '$BUILD_DIR/mongo/db/service_context_noop_init', '$BUILD_DIR/mongo/s/catalog_cache_test_fixture', ], diff --git a/src/mongo/s/query/SConscript b/src/mongo/s/query/SConscript index aa9a1713255..cd9a6c903c4 100644 --- a/src/mongo/s/query/SConscript +++ b/src/mongo/s/query/SConscript @@ -29,7 +29,7 @@ env.CppUnitTest( ], LIBDEPS=[ 'cluster_query', - '$BUILD_DIR/mongo/db/auth/authorization_manager_mock_init', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/service_context_noop_init', '$BUILD_DIR/mongo/s/catalog_cache_test_fixture', ], @@ -76,7 +76,7 @@ env.CppUnitTest( ], LIBDEPS=[ 'router_exec_stage', - '$BUILD_DIR/mongo/db/auth/authorization_manager_mock_init', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/service_context_noop_init', ], ) @@ -115,7 +115,7 @@ env.CppUnitTest( LIBDEPS=[ 'async_results_merger', 'cluster_client_cursor', - '$BUILD_DIR/mongo/db/auth/authorization_manager_mock_init', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/query/query_request', '$BUILD_DIR/mongo/db/service_context_noop_init', '$BUILD_DIR/mongo/executor/thread_pool_task_executor_test_fixture', @@ -130,7 +130,7 @@ env.CppUnitTest( ], LIBDEPS=[ 'async_results_merger', - '$BUILD_DIR/mongo/db/auth/authorization_manager_mock_init', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/query/query_request', '$BUILD_DIR/mongo/db/service_context_noop_init', '$BUILD_DIR/mongo/s/sharding_router_test_fixture', @@ -157,7 +157,7 @@ env.CppUnitTest( ], LIBDEPS=[ 'store_possible_cursor', - '$BUILD_DIR/mongo/db/auth/authorization_manager_mock_init', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/query/query_test_service_context', '$BUILD_DIR/mongo/db/service_context_noop_init', '$BUILD_DIR/mongo/util/clock_source_mock', @@ -171,7 +171,8 @@ env.Library( ], LIBDEPS=[ '$BUILD_DIR/mongo/base', - '$BUILD_DIR/mongo/db/auth/authcore', + '$BUILD_DIR/mongo/db/auth/auth', + '$BUILD_DIR/mongo/db/auth/authprivilege', '$BUILD_DIR/mongo/db/generic_cursor', '$BUILD_DIR/mongo/db/kill_sessions', '$BUILD_DIR/mongo/db/logical_session_cache', @@ -211,7 +212,7 @@ env.CppUnitTest( ], LIBDEPS=[ 'cluster_client_cursor', - '$BUILD_DIR/mongo/db/auth/authorization_manager_mock_init', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/db/service_context_noop_init', ], ) diff --git a/src/mongo/s/server.cpp b/src/mongo/s/server.cpp index 3c68ef51462..7d8f848921b 100644 --- a/src/mongo/s/server.cpp +++ b/src/mongo/s/server.cpp @@ -528,6 +528,7 @@ ExitCode main(ServiceContext* serviceContext) { return runMongosServer(serviceContext); } +namespace { MONGO_INITIALIZER_GENERAL(ForkServer, ("EndStartupOptionHandling"), ("default")) (InitializerContext* context) { forkServerOrDie(); @@ -546,11 +547,6 @@ MONGO_INITIALIZER_WITH_PREREQUISITES(SetFeatureCompatibilityVersion40, ("EndStar return Status::OK(); } -MONGO_INITIALIZER(CreateAuthorizationExternalStateFactory)(InitializerContext* context) { - AuthzManagerExternalState::create = &createAuthzManagerExternalStateMongos; - return Status::OK(); -} - ServiceContextRegistrar serviceContextCreator([]() { auto service = std::make_unique<ServiceContextNoop>(); service->setTickSource(std::make_unique<SystemTickSource>()); @@ -559,7 +555,6 @@ ServiceContextRegistrar serviceContextCreator([]() { return service; }); - #ifdef MONGO_CONFIG_SSL MONGO_INITIALIZER_GENERAL(setSSLManagerType, MONGO_NO_PREREQUISITES, ("SSLManager")) (InitializerContext* context) { @@ -612,6 +607,7 @@ ExitCode mongoSMain(int argc, char* argv[], char** envp) { } } +} // namespace } // namespace mongo #if defined(_WIN32) diff --git a/src/mongo/s/write_ops/SConscript b/src/mongo/s/write_ops/SConscript index b9dda858343..fd10368b310 100644 --- a/src/mongo/s/write_ops/SConscript +++ b/src/mongo/s/write_ops/SConscript @@ -70,7 +70,7 @@ env.CppUnitTest( 'write_op_test.cpp', ], LIBDEPS=[ - '$BUILD_DIR/mongo/db/auth/authorization_manager_mock_init', + '$BUILD_DIR/mongo/db/auth/authmocks', '$BUILD_DIR/mongo/s/sharding_router_test_fixture', 'cluster_write_op', ] diff --git a/src/mongo/shell/shell_utils.cpp b/src/mongo/shell/shell_utils.cpp index 962e4ef2a43..5a9742d3935 100644 --- a/src/mongo/shell/shell_utils.cpp +++ b/src/mongo/shell/shell_utils.cpp @@ -1,4 +1,3 @@ -// mongo/shell/shell_utils.cpp /* * Copyright 2010 10gen Inc. * @@ -443,4 +442,4 @@ bool fileExists(const std::string& file) { stdx::mutex& mongoProgramOutputMutex(*(new stdx::mutex())); } -} +} // namespace mongo |