diff options
author | Spencer Jackson <spencer.jackson@mongodb.com> | 2016-08-08 11:51:13 -0400 |
---|---|---|
committer | Spencer Jackson <spencer.jackson@mongodb.com> | 2016-08-09 19:37:44 -0400 |
commit | d930f4832631eca7092ada4328d780f2b8d19d31 (patch) | |
tree | 0b3920a9fd2172ed503f2ad4a1d9979a852d7fd3 /src/mongo/db | |
parent | 4c6009e67d3e503f796b5afcbcbeaa95eba80b44 (diff) | |
download | mongo-d930f4832631eca7092ada4328d780f2b8d19d31.tar.gz |
SERVER-22826 Support X509 Authorization
Diffstat (limited to 'src/mongo/db')
-rw-r--r-- | src/mongo/db/auth/SConscript | 17 | ||||
-rw-r--r-- | src/mongo/db/auth/authorization_manager.cpp | 16 | ||||
-rw-r--r-- | src/mongo/db/auth/authorization_manager.h | 45 | ||||
-rw-r--r-- | src/mongo/db/auth/authorization_manager_test.cpp | 63 | ||||
-rw-r--r-- | src/mongo/db/auth/authz_manager_external_state.cpp | 12 | ||||
-rw-r--r-- | src/mongo/db/auth/authz_manager_external_state.h | 75 | ||||
-rw-r--r-- | src/mongo/db/auth/authz_manager_external_state_local.cpp | 81 | ||||
-rw-r--r-- | src/mongo/db/auth/authz_manager_external_state_local.h | 10 | ||||
-rw-r--r-- | src/mongo/db/auth/authz_manager_external_state_s.cpp | 211 | ||||
-rw-r--r-- | src/mongo/db/auth/authz_manager_external_state_s.h | 8 | ||||
-rw-r--r-- | src/mongo/db/auth/privilege_format.h | 42 | ||||
-rw-r--r-- | src/mongo/db/auth/sasl_authentication_session.cpp | 1 | ||||
-rw-r--r-- | src/mongo/db/auth/user_management_commands_parser.cpp | 18 | ||||
-rw-r--r-- | src/mongo/db/auth/user_management_commands_parser.h | 6 | ||||
-rw-r--r-- | src/mongo/db/commands/authentication_commands.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/commands/user_management_commands.cpp | 91 |
16 files changed, 543 insertions, 155 deletions
diff --git a/src/mongo/db/auth/SConscript b/src/mongo/db/auth/SConscript index 93a2091e343..555d21845ea 100644 --- a/src/mongo/db/auth/SConscript +++ b/src/mongo/db/auth/SConscript @@ -10,6 +10,12 @@ generateActionTypes = env.Command( action='$PYTHON $SOURCES $TARGETS') env.Alias('generated-sources', generateActionTypes) +env.Library('auth_rolename', ['role_name.cpp'], + LIBDEPS=[ + '$BUILD_DIR/mongo/base', + ] +) + # Just the data structures used env.Library('authcore', ['action_set.cpp', 'action_type.cpp', @@ -24,14 +30,14 @@ env.Library('authcore', ['action_set.cpp', 'resource_pattern.cpp', 'role_graph.cpp', 'role_graph_update.cpp', - 'role_name.cpp', 'role_graph_builtin_roles.cpp', 'user.cpp', 'user_document_parser.cpp', 'user_management_commands_parser.cpp', 'user_name.cpp', 'user_set.cpp'], - LIBDEPS=['sasl_options', + LIBDEPS=['auth_rolename', + 'sasl_options', '$BUILD_DIR/mongo/base', '$BUILD_DIR/mongo/bson/mutable/mutable_bson', '$BUILD_DIR/mongo/bson/util/bson_extract', @@ -174,6 +180,11 @@ env.CppUnitTest('user_document_parser_test', 'user_document_parser_test.cpp', env.CppUnitTest('user_set_test', 'user_set_test.cpp', LIBDEPS=['authcore', 'authmocks', 'saslauth']) env.CppUnitTest('authorization_manager_test', 'authorization_manager_test.cpp', - LIBDEPS=['authcore', 'authmocks', 'saslauth']) + LIBDEPS=[ + '$BUILD_DIR/mongo/transport/transport_layer_common', + '$BUILD_DIR/mongo/transport/transport_layer_mock', + 'authcore', + 'authmocks', + 'saslauth']) env.CppUnitTest('authorization_session_test', 'authorization_session_test.cpp', LIBDEPS=['authcore', 'authmocks', 'saslauth']) diff --git a/src/mongo/db/auth/authorization_manager.cpp b/src/mongo/db/auth/authorization_manager.cpp index 131eeda34c6..8f404e7ed8c 100644 --- a/src/mongo/db/auth/authorization_manager.cpp +++ b/src/mongo/db/auth/authorization_manager.cpp @@ -422,18 +422,26 @@ Status AuthorizationManager::getUserDescription(OperationContext* txn, Status AuthorizationManager::getRoleDescription(OperationContext* txn, const RoleName& roleName, - bool showPrivileges, + PrivilegeFormat privileges, BSONObj* result) { - return _externalState->getRoleDescription(txn, roleName, showPrivileges, result); + return _externalState->getRoleDescription(txn, roleName, privileges, result); } +Status AuthorizationManager::getRolesDescription(OperationContext* txn, + const std::vector<RoleName>& roleName, + PrivilegeFormat privileges, + BSONObj* result) { + return _externalState->getRolesDescription(txn, roleName, privileges, result); +} + + Status AuthorizationManager::getRoleDescriptionsForDB(OperationContext* txn, const std::string dbname, - bool showPrivileges, + PrivilegeFormat privileges, bool showBuiltinRoles, vector<BSONObj>* result) { return _externalState->getRoleDescriptionsForDB( - txn, dbname, showPrivileges, showBuiltinRoles, result); + txn, dbname, privileges, showBuiltinRoles, result); } Status AuthorizationManager::acquireUser(OperationContext* txn, diff --git a/src/mongo/db/auth/authorization_manager.h b/src/mongo/db/auth/authorization_manager.h index 43b1741841b..b12abccad40 100644 --- a/src/mongo/db/auth/authorization_manager.h +++ b/src/mongo/db/auth/authorization_manager.h @@ -36,6 +36,7 @@ #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" @@ -219,50 +220,32 @@ public: ActionSet getActionsForOldStyleUser(const std::string& dbname, bool readOnly) const; /** - * Writes into "result" a document describing the named user and returns Status::OK(). The - * description includes the user credentials and customData, if present, the user's role - * membership and delegation information, a full list of the user's privileges, and a full - * list of the user's roles, including those roles held implicitly through other roles - * (indirect roles). In the event that some of this information is inconsistent, the - * document will contain a "warnings" array, with std::string messages describing - * inconsistencies. - * - * If the user does not exist, returns ErrorCodes::UserNotFound. + * Delegates method call to the underlying AuthzManagerExternalState. */ Status getUserDescription(OperationContext* txn, const UserName& userName, BSONObj* result); /** - * Writes into "result" a document describing the named role and returns Status::OK(). The - * description includes the roles in which the named role has membership and a full list of - * the roles of which the named role is a member, including those roles memberships held - * implicitly through other roles (indirect roles). If "showPrivileges" is true, then the - * description documents will also include a full list of the role's privileges. - * In the event that some of this information is inconsistent, the document will contain a - * "warnings" array, with std::string messages describing inconsistencies. - * - * If the role does not exist, returns ErrorCodes::RoleNotFound. + * Delegates method call to the underlying AuthzManagerExternalState. */ Status getRoleDescription(OperationContext* txn, const RoleName& roleName, - bool showPrivileges, + PrivilegeFormat privilegeFormat, BSONObj* result); /** - * Writes into "result" documents describing the roles that are defined on the given - * database. Each role description document includes the other roles in which the role has - * membership and a full list of the roles of which the named role is a member, - * including those roles memberships held implicitly through other roles (indirect roles). - * If showPrivileges is true, then the description documents will also include a full list - * of the role's privileges. If showBuiltinRoles is true, then the result array will - * contain description documents for all the builtin roles for the given database, if it - * is false the result will just include user defined roles. - * In the event that some of the information in a given role description is inconsistent, - * the document will contain a "warnings" array, with std::string messages describing - * inconsistencies. + * Delegates method call to the underlying AuthzManagerExternalState. + */ + Status getRolesDescription(OperationContext* txn, + const std::vector<RoleName>& roleName, + PrivilegeFormat privilegeFormat, + BSONObj* result); + + /** + * Delegates method call to the underlying AuthzManagerExternalState. */ Status getRoleDescriptionsForDB(OperationContext* txn, const std::string dbname, - bool showPrivileges, + PrivilegeFormat privilegeFormat, bool showBuiltinRoles, std::vector<BSONObj>* result); diff --git a/src/mongo/db/auth/authorization_manager_test.cpp b/src/mongo/db/auth/authorization_manager_test.cpp index cd2b83fa6b1..56afbd26d40 100644 --- a/src/mongo/db/auth/authorization_manager_test.cpp +++ b/src/mongo/db/auth/authorization_manager_test.cpp @@ -41,9 +41,13 @@ #include "mongo/db/jsobj.h" #include "mongo/db/namespace_string.h" #include "mongo/db/operation_context_noop.h" +#include "mongo/db/service_context_noop.h" #include "mongo/stdx/memory.h" +#include "mongo/transport/session.h" +#include "mongo/transport/transport_layer_mock.h" #include "mongo/unittest/unittest.h" #include "mongo/util/map_util.h" +#include "mongo/util/net/message_port.h" #define ASSERT_NULL(EXPR) ASSERT_FALSE(EXPR) #define ASSERT_NON_NULL(EXPR) ASSERT_TRUE(EXPR) @@ -237,6 +241,65 @@ TEST_F(AuthorizationManagerTest, testAcquireV2User) { authzManager->releaseUser(v2cluster); } +TEST_F(AuthorizationManagerTest, testLocalX509Authorization) { + ServiceContextNoop serviceContext; + transport::TransportLayerMock transportLayer{}; + transport::Session* session = transportLayer.createSession(); + transportLayer.setX509PeerInfo( + *session, + SSLPeerInfo("CN=mongodb.com", {RoleName("read", "test"), RoleName("readWrite", "test")})); + ServiceContext::UniqueClient client = serviceContext.makeClient("testClient", session); + ServiceContext::UniqueOperationContext txn = client->makeOperationContext(); + + User* x509User; + ASSERT_OK( + authzManager->acquireUser(txn.get(), UserName("CN=mongodb.com", "$external"), &x509User)); + ASSERT(x509User->isValid()); + + RoleNameIterator roles = x509User->getRoles(); + ASSERT_EQUALS(RoleName("read", "test"), roles.next()); + ASSERT_EQUALS(RoleName("readWrite", "test"), roles.next()); + ASSERT_FALSE(roles.more()); + + + const User::ResourcePrivilegeMap& privileges = x509User->getPrivileges(); + ASSERT_FALSE(privileges.empty()); + auto privilegeIt = privileges.find(ResourcePattern::forDatabaseName("test")); + ASSERT(privilegeIt != privileges.end()); + ASSERT(privilegeIt->second.includesAction(ActionType::insert)); + + + authzManager->releaseUser(x509User); +} + +TEST_F(AuthorizationManagerTest, testLocalX509AuthorizationInvalidUser) { + ServiceContextNoop serviceContext; + transport::TransportLayerMock transportLayer{}; + transport::Session* session = transportLayer.createSession(); + transportLayer.setX509PeerInfo( + *session, + SSLPeerInfo("CN=mongodb.com", {RoleName("read", "test"), RoleName("write", "test")})); + ServiceContext::UniqueClient client = serviceContext.makeClient("testClient", session); + ServiceContext::UniqueOperationContext txn = client->makeOperationContext(); + + User* x509User; + ASSERT_NOT_OK( + authzManager->acquireUser(txn.get(), UserName("CN=10gen.com", "$external"), &x509User)); +} + +TEST_F(AuthorizationManagerTest, testLocalX509AuthenticationNoAuthorization) { + ServiceContextNoop serviceContext; + transport::TransportLayerMock transportLayer{}; + transport::Session* session = transportLayer.createSession(); + transportLayer.setX509PeerInfo(*session, {}); + ServiceContext::UniqueClient client = serviceContext.makeClient("testClient", session); + ServiceContext::UniqueOperationContext txn = client->makeOperationContext(); + + User* x509User; + ASSERT_NOT_OK( + authzManager->acquireUser(txn.get(), UserName("CN=mongodb.com", "$external"), &x509User)); +} + /** * An implementation of AuthzManagerExternalStateMock that overrides the getUserDescription method * to return the user document unmodified from how it was inserted. When using this insert user diff --git a/src/mongo/db/auth/authz_manager_external_state.cpp b/src/mongo/db/auth/authz_manager_external_state.cpp index fae54cbf5e9..ed5f0fe6bfd 100644 --- a/src/mongo/db/auth/authz_manager_external_state.cpp +++ b/src/mongo/db/auth/authz_manager_external_state.cpp @@ -29,6 +29,9 @@ #include "mongo/platform/basic.h" #include "mongo/db/auth/authz_manager_external_state.h" +#include "mongo/db/auth/user_name.h" +#include "mongo/db/operation_context.h" +#include "mongo/util/net/ssl_types.h" namespace mongo { @@ -37,4 +40,13 @@ stdx::function<std::unique_ptr<AuthzManagerExternalState>()> AuthzManagerExterna AuthzManagerExternalState::AuthzManagerExternalState() = default; AuthzManagerExternalState::~AuthzManagerExternalState() = default; +bool AuthzManagerExternalState::shouldUseRolesFromConnection(OperationContext* txn, + const UserName& userName) { + return txn && txn->getClient() && txn->getClient()->session() && + txn->getClient()->session()->getX509PeerInfo().subjectName == userName.getUser() && + userName.getDB() == "$external" && + !txn->getClient()->session()->getX509PeerInfo().roles.empty(); +} + + } // namespace mongo diff --git a/src/mongo/db/auth/authz_manager_external_state.h b/src/mongo/db/auth/authz_manager_external_state.h index 78aac5ea832..f3d2cc721c6 100644 --- a/src/mongo/db/auth/authz_manager_external_state.h +++ b/src/mongo/db/auth/authz_manager_external_state.h @@ -34,7 +34,9 @@ #include "mongo/base/disallow_copying.h" #include "mongo/base/status.h" +#include "mongo/db/auth/privilege_format.h" #include "mongo/db/auth/role_name.h" +#include "mongo/db/auth/user.h" #include "mongo/db/auth/user_name.h" #include "mongo/db/jsobj.h" #include "mongo/stdx/functional.h" @@ -80,11 +82,12 @@ public: /** * Writes into "result" a document describing the named user and returns Status::OK(). The - * description includes the user credentials, if present, the user's role membership and - * delegation information, a full list of the user's privileges, and a full list of the - * user's roles, including those roles held implicitly through other roles (indirect roles). - * In the event that some of this information is inconsistent, the document will contain a - * "warnings" array, with std::string messages describing inconsistencies. + * description includes the user credentials and customData, if present, the user's role + * membership and delegation information, a full list of the user's privileges, and a full + * list of the user's roles, including those roles held implicitly through other roles + * (indirect roles). In the event that some of this information is inconsistent, the + * document will contain a "warnings" array, with std::string messages describing + * inconsistencies. * * If the user does not exist, returns ErrorCodes::UserNotFound. */ @@ -93,37 +96,57 @@ public: BSONObj* result) = 0; /** - * Writes into "result" a document describing the named role and returns Status::OK(). The - * description includes the roles in which the named role has membership and a full list of - * the roles of which the named role is a member, including those roles memberships held - * implicitly through other roles (indirect roles). If "showPrivileges" is true, then the - * description documents will also include a full list of the role's privileges. - * In the event that some of this information is inconsistent, the document will contain a - * "warnings" array, with std::string messages describing inconsistencies. + * Writes into "result" a document describing the named role and returns Status::OK(). If + * showPrivileges is kOmit or kShowPrivileges, the description includes the roles which the + * named role is a member of, including those memberships held implicitly through other roles + * (indirect roles). If "showPrivileges" is kShowPrivileges, then the description documents + * will also include a full list of the role's privileges. If "showPrivileges" is + * kShowAsUserFragment, then the description returned will take the form of a partial user + * document, describing a hypothetical user which possesses the provided and implicit roles, + * and all inherited privileges. In the event that some of this information is inconsistent, + * the document will contain a "warnings" array, with std::string messages describing + * inconsistencies. * * If the role does not exist, returns ErrorCodes::RoleNotFound. */ virtual Status getRoleDescription(OperationContext* txn, const RoleName& roleName, - bool showPrivileges, + PrivilegeFormat showPrivileges, BSONObj* result) = 0; /** - * Writes into "result" documents describing the roles that are defined on the given - * database. Each role description document includes the other roles in which the role has - * membership and a full list of the roles of which the named role is a member, - * including those roles memberships held implicitly through other roles (indirect roles). - * If showPrivileges is true, then the description documents will also include a full list - * of the role's privileges. If showBuiltinRoles is true, then the result array will - * contain description documents for all the builtin roles for the given database, if it - * is false the result will just include user defined roles. - * In the event that some of the information in a given role description is inconsistent, + * Writes into "result" a document describing the named role is and returns Status::OK(). If + * showPrivileges is kOmit or kShowPrivileges, the description includes the roles which the + * named roles are a member of, including those memberships held implicitly through other roles + * (indirect roles). If "showPrivileges" is kShowPrivileges, then the description documents + * will also include a full list of the roles' privileges. If "showPrivileges" is + * kShowAsUserFragment, then the description returned will take the form of a partial user + * document, describing a hypothetical user which possesses the provided and implicit roles, + * and all inherited privileges. In the event that some of this information is inconsistent, * the document will contain a "warnings" array, with std::string messages describing * inconsistencies. */ + + virtual Status getRolesDescription(OperationContext* txn, + const std::vector<RoleName>& roles, + PrivilegeFormat showPrivileges, + BSONObj* result) = 0; + + /** + * Writes into "result" documents describing the roles that are defined on the given + * database. If showPrivileges is kOmit or kShowPrivileges, then a vector of BSON documents are + * returned, where each document includes the other roles a particular role is a + * member of, including those role memberships held implicitly through other roles + * (indirect roles). If showPrivileges is kShowPrivileges, then the description documents + * will also include a full list of the roles' privileges. If showBuiltinRoles is true, then + * the result array will contain description documents for all the builtin roles for the given + * database, if it is false the result will just include user defined roles. In the event that + * some of the information in a given role description is inconsistent, the document will + * contain a "warnings" array, with std::string messages describing inconsistencies. + */ virtual Status getRoleDescriptionsForDB(OperationContext* txn, const std::string dbname, - bool showPrivileges, + PrivilegeFormat showPrivileges, bool showBuiltinRoles, std::vector<BSONObj>* result) = 0; @@ -141,6 +164,12 @@ public: protected: AuthzManagerExternalState(); // This class should never be instantiated directly. + + /** + * Returns true if roles for this user were provided by the client, and can be obtained from + * the connection. + */ + bool shouldUseRolesFromConnection(OperationContext* txn, const UserName& username); }; } // namespace mongo diff --git a/src/mongo/db/auth/authz_manager_external_state_local.cpp b/src/mongo/db/auth/authz_manager_external_state_local.cpp index 82bd5c29440..d4f814baae5 100644 --- a/src/mongo/db/auth/authz_manager_external_state_local.cpp +++ b/src/mongo/db/auth/authz_manager_external_state_local.cpp @@ -152,9 +152,25 @@ bool AuthzManagerExternalStateLocal::hasAnyPrivilegeDocuments(OperationContext* Status AuthzManagerExternalStateLocal::getUserDescription(OperationContext* txn, const UserName& userName, BSONObj* result) { - Status status = _getUserDocument(txn, userName, result); - if (!status.isOK()) - return status; + Status status = Status::OK(); + + if (!shouldUseRolesFromConnection(txn, userName)) { + status = _getUserDocument(txn, userName, result); + if (!status.isOK()) + return status; + } else { + // We are able to artifically construct the external user from the request + BSONArrayBuilder userRoles; + for (const RoleName& role : txn->getClient()->session()->getX509PeerInfo().roles) { + userRoles << BSON("role" << role.getRole() << "db" << role.getDB()); + } + *result = BSON("_id" << userName.getUser() << "user" << userName.getUser() << "db" + << userName.getDB() + << "credentials" + << BSON("external" << true) + << "roles" + << userRoles.arr()); + } BSONElement directRolesElement; status = bsonExtractTypedField(*result, "roles", Array, &directRolesElement); @@ -241,14 +257,55 @@ Status AuthzManagerExternalStateLocal::_getUserDocument(OperationContext* txn, Status AuthzManagerExternalStateLocal::getRoleDescription(OperationContext* txn, const RoleName& roleName, - bool showPrivileges, + PrivilegeFormat showPrivileges, BSONObj* result) { + if (showPrivileges == PrivilegeFormat::kShowAsUserFragment) { + mutablebson::Document resultDoc; + mutablebson::Element rolesElement = resultDoc.makeElementArray("roles"); + fassert(40271, resultDoc.root().pushBack(rolesElement)); + addRoleNameObjectsToArrayElement( + rolesElement, makeRoleNameIteratorForContainer(std::vector<RoleName>{roleName})); + resolveUserRoles(&resultDoc, {roleName}); + *result = resultDoc.getObject(); + return Status::OK(); + } stdx::lock_guard<stdx::mutex> lk(_roleGraphMutex); return _getRoleDescription_inlock(roleName, showPrivileges, result); } +Status AuthzManagerExternalStateLocal::getRolesDescription(OperationContext* txn, + const std::vector<RoleName>& roles, + PrivilegeFormat showPrivileges, + BSONObj* result) { + if (showPrivileges == PrivilegeFormat::kShowAsUserFragment) { + mutablebson::Document resultDoc; + mutablebson::Element rolesElement = resultDoc.makeElementArray("roles"); + fassert(40272, resultDoc.root().pushBack(rolesElement)); + addRoleNameObjectsToArrayElement(rolesElement, makeRoleNameIteratorForContainer(roles)); + resolveUserRoles(&resultDoc, roles); + *result = resultDoc.getObject(); + return Status::OK(); + } + + stdx::lock_guard<stdx::mutex> lk(_roleGraphMutex); + BSONArrayBuilder resultBuilder; + for (const RoleName& role : roles) { + BSONObj roleDoc; + Status status = _getRoleDescription_inlock(role, showPrivileges, &roleDoc); + if (!status.isOK()) { + if (status.code() == ErrorCodes::RoleNotFound) { + continue; + } + return status; + } + resultBuilder << roleDoc; + } + *result = resultBuilder.arr(); + return Status::OK(); +} + Status AuthzManagerExternalStateLocal::_getRoleDescription_inlock(const RoleName& roleName, - bool showPrivileges, + PrivilegeFormat showPrivileges, BSONObj* result) { if (!_roleGraph.roleExists(roleName)) return Status(ErrorCodes::RoleNotFound, "No role named " + roleName.toString()); @@ -268,7 +325,7 @@ Status AuthzManagerExternalStateLocal::_getRoleDescription_inlock(const RoleName mutablebson::Element privilegesElement = resultDoc.makeElementArray("privileges"); mutablebson::Element inheritedPrivilegesElement = resultDoc.makeElementArray("inheritedPrivileges"); - if (showPrivileges) { + if (showPrivileges == PrivilegeFormat::kShowSeparate) { fassert(17166, resultDoc.root().pushBack(privilegesElement)); } mutablebson::Element warningsElement = resultDoc.makeElementArray("warnings"); @@ -277,7 +334,7 @@ Status AuthzManagerExternalStateLocal::_getRoleDescription_inlock(const RoleName if (_roleGraphState == roleGraphStateConsistent) { addRoleNameObjectsToArrayElement(inheritedRolesElement, _roleGraph.getIndirectSubordinates(roleName)); - if (showPrivileges) { + if (showPrivileges == PrivilegeFormat::kShowSeparate) { addPrivilegeObjectsOrWarningsToArrayElement( privilegesElement, warningsElement, _roleGraph.getDirectPrivileges(roleName)); @@ -286,7 +343,7 @@ Status AuthzManagerExternalStateLocal::_getRoleDescription_inlock(const RoleName fassert(17323, resultDoc.root().pushBack(inheritedPrivilegesElement)); } - } else if (showPrivileges) { + } else if (showPrivileges == PrivilegeFormat::kShowSeparate) { warningsElement.appendString( "", "Role graph state inconsistent; only direct privileges available."); addPrivilegeObjectsOrWarningsToArrayElement( @@ -301,11 +358,15 @@ Status AuthzManagerExternalStateLocal::_getRoleDescription_inlock(const RoleName Status AuthzManagerExternalStateLocal::getRoleDescriptionsForDB(OperationContext* txn, const std::string dbname, - bool showPrivileges, + PrivilegeFormat showPrivileges, bool showBuiltinRoles, vector<BSONObj>* result) { - stdx::lock_guard<stdx::mutex> lk(_roleGraphMutex); + if (showPrivileges == PrivilegeFormat::kShowAsUserFragment) { + return Status(ErrorCodes::IllegalOperation, + "Cannot get user fragment for all roles in a database"); + } + stdx::lock_guard<stdx::mutex> lk(_roleGraphMutex); for (RoleNameIterator it = _roleGraph.getRolesForDatabase(dbname); it.more(); it.next()) { if (!showBuiltinRoles && _roleGraph.isBuiltinRole(it.get())) { continue; diff --git a/src/mongo/db/auth/authz_manager_external_state_local.h b/src/mongo/db/auth/authz_manager_external_state_local.h index c9646686b03..e1e2cbdc1e2 100644 --- a/src/mongo/db/auth/authz_manager_external_state_local.h +++ b/src/mongo/db/auth/authz_manager_external_state_local.h @@ -63,11 +63,15 @@ public: BSONObj* result); virtual Status getRoleDescription(OperationContext* txn, const RoleName& roleName, - bool showPrivileges, + PrivilegeFormat showPrivileges, BSONObj* result); + virtual Status getRolesDescription(OperationContext* txn, + const std::vector<RoleName>& roles, + PrivilegeFormat showPrivileges, + BSONObj* result); virtual Status getRoleDescriptionsForDB(OperationContext* txn, const std::string dbname, - bool showPrivileges, + PrivilegeFormat showPrivileges, bool showBuiltinRoles, std::vector<BSONObj>* result); @@ -131,7 +135,7 @@ private: Status _getUserDocument(OperationContext* txn, const UserName& userName, BSONObj* result); Status _getRoleDescription_inlock(const RoleName& roleName, - bool showPrivileges, + PrivilegeFormat showPrivileges, BSONObj* result); /** * Eventually consistent, in-memory representation of all roles in the system (both 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 500cc85c70a..06831cf9a58 100644 --- a/src/mongo/db/auth/authz_manager_external_state_s.cpp +++ b/src/mongo/db/auth/authz_manager_external_state_s.cpp @@ -38,16 +38,47 @@ #include "mongo/db/auth/authorization_manager.h" #include "mongo/db/auth/authorization_manager_global.h" #include "mongo/db/auth/authz_session_external_state_s.h" +#include "mongo/db/auth/user_document_parser.h" +#include "mongo/db/auth/user_management_commands_parser.h" #include "mongo/db/auth/user_name.h" #include "mongo/db/jsobj.h" +#include "mongo/db/operation_context.h" #include "mongo/rpc/get_status_from_command_result.h" #include "mongo/s/catalog/sharding_catalog_client.h" #include "mongo/s/grid.h" #include "mongo/stdx/memory.h" #include "mongo/util/mongoutils/str.h" +#include "mongo/util/stringutils.h" namespace mongo { +namespace { + +/** + * Returns the top level field which is expected to be returned by rolesInfo. + */ +std::string rolesFieldName(PrivilegeFormat showPrivileges) { + if (showPrivileges == PrivilegeFormat::kShowAsUserFragment) { + return "userFragment"; + } + return "roles"; +} + +/** + * Attches a string representation of a PrivilegeFormat to the provided BSONObjBuilder. + */ +void addShowPrivilegesToBuilder(BSONObjBuilder* builder, PrivilegeFormat showPrivileges) { + if (showPrivileges == PrivilegeFormat::kShowSeparate) { + builder->append("showPrivileges", true); + } else if (showPrivileges == PrivilegeFormat::kShowAsUserFragment) { + builder->append("showPrivileges", "asUserfragment"); + } else { + builder->append("showPrivileges", false); + } +} + +} // namespace + AuthzManagerExternalStateMongos::AuthzManagerExternalStateMongos() = default; AuthzManagerExternalStateMongos::~AuthzManagerExternalStateMongos() = default; @@ -88,59 +119,111 @@ Status AuthzManagerExternalStateMongos::getStoredAuthorizationVersion(OperationC Status AuthzManagerExternalStateMongos::getUserDescription(OperationContext* txn, const UserName& userName, BSONObj* result) { - BSONObj usersInfoCmd = - BSON("usersInfo" << BSON_ARRAY(BSON(AuthorizationManager::USER_NAME_FIELD_NAME - << userName.getUser() - << AuthorizationManager::USER_DB_FIELD_NAME - << userName.getDB())) - << "showPrivileges" - << true - << "showCredentials" - << true); - BSONObjBuilder builder; - const bool ok = - grid.catalogClient(txn)->runUserManagementReadCommand(txn, "admin", usersInfoCmd, &builder); - BSONObj cmdResult = builder.obj(); - if (!ok) { - return getStatusFromCommandResult(cmdResult); - } + if (!shouldUseRolesFromConnection(txn, userName)) { + BSONObj usersInfoCmd = + BSON("usersInfo" << BSON_ARRAY(BSON(AuthorizationManager::USER_NAME_FIELD_NAME + << userName.getUser() + << AuthorizationManager::USER_DB_FIELD_NAME + << userName.getDB())) + << "showPrivileges" + << true + << "showCredentials" + << true); + BSONObjBuilder builder; + const bool ok = grid.catalogClient(txn)->runUserManagementReadCommand( + txn, "admin", usersInfoCmd, &builder); + BSONObj cmdResult = builder.obj(); + if (!ok) { + return getStatusFromCommandResult(cmdResult); + } - std::vector<BSONElement> foundUsers = cmdResult["users"].Array(); - if (foundUsers.size() == 0) { - return Status(ErrorCodes::UserNotFound, "User \"" + userName.toString() + "\" not found"); - } + std::vector<BSONElement> foundUsers = cmdResult["users"].Array(); + if (foundUsers.size() == 0) { + return Status(ErrorCodes::UserNotFound, + "User \"" + userName.toString() + "\" not found"); + } - if (foundUsers.size() > 1) { - return Status(ErrorCodes::UserDataInconsistent, - str::stream() << "Found multiple users on the \"" << userName.getDB() - << "\" database with name \"" - << userName.getUser() - << "\""); + if (foundUsers.size() > 1) { + return Status(ErrorCodes::UserDataInconsistent, + str::stream() << "Found multiple users on the \"" << userName.getDB() + << "\" database with name \"" + << userName.getUser() + << "\""); + } + *result = foundUsers[0].Obj().getOwned(); + return Status::OK(); + } else { + // Obtain privilege information from the config servers for all roles acquired from the X509 + // certificate. + BSONArrayBuilder userRolesBuilder; + for (const RoleName& role : txn->getClient()->session()->getX509PeerInfo().roles) { + userRolesBuilder.append(BSON(AuthorizationManager::ROLE_NAME_FIELD_NAME + << role.getRole() + << AuthorizationManager::ROLE_DB_FIELD_NAME + << role.getDB())); + } + BSONArray providedRoles = userRolesBuilder.arr(); + + BSONObj rolesInfoCmd = BSON("rolesInfo" << providedRoles << "showPrivileges" + << "asUserFragment"); + + BSONObjBuilder cmdResultBuilder; + const bool cmdOk = grid.catalogClient(txn)->runUserManagementReadCommand( + txn, "admin", rolesInfoCmd, &cmdResultBuilder); + BSONObj cmdResult = cmdResultBuilder.obj(); + if (!cmdOk || !cmdResult["userFragment"].ok()) { + return Status(ErrorCodes::FailedToParse, + "Unable to get resolved X509 roles from config server: " + + getStatusFromCommandResult(cmdResult).toString()); + } + cmdResult = cmdResult["userFragment"].Obj().getOwned(); + BSONElement userRoles = cmdResult["roles"]; + BSONElement userInheritedRoles = cmdResult["inheritedRoles"]; + BSONElement userInheritedPrivileges = cmdResult["inheritedPrivileges"]; + + if (userRoles.eoo() || userInheritedRoles.eoo() || userInheritedPrivileges.eoo() || + !userRoles.isABSONObj() || !userInheritedRoles.isABSONObj() || + !userInheritedPrivileges.isABSONObj()) { + return Status( + ErrorCodes::UserDataInconsistent, + "Recieved malformed response to request for X509 roles from config server"); + } + + *result = BSON("_id" << userName.getUser() << "user" << userName.getUser() << "db" + << userName.getDB() + << "credentials" + << BSON("external" << true) + << "roles" + << BSONArray(cmdResult["roles"].Obj()) + << "inheritedRoles" + << BSONArray(cmdResult["inheritedRoles"].Obj()) + << "inheritedPrivileges" + << BSONArray(cmdResult["inheritedPrivileges"].Obj())); + return Status::OK(); } - *result = foundUsers[0].Obj().getOwned(); - return Status::OK(); } Status AuthzManagerExternalStateMongos::getRoleDescription(OperationContext* txn, const RoleName& roleName, - bool showPrivileges, + PrivilegeFormat showPrivileges, BSONObj* result) { - BSONObj rolesInfoCmd = - BSON("rolesInfo" << BSON_ARRAY(BSON(AuthorizationManager::ROLE_NAME_FIELD_NAME - << roleName.getRole() - << AuthorizationManager::ROLE_DB_FIELD_NAME - << roleName.getDB())) - << "showPrivileges" - << showPrivileges); + BSONObjBuilder rolesInfoCmd; + rolesInfoCmd.append("rolesInfo", + BSON_ARRAY(BSON(AuthorizationManager::ROLE_NAME_FIELD_NAME + << roleName.getRole() + << AuthorizationManager::ROLE_DB_FIELD_NAME + << roleName.getDB()))); + addShowPrivilegesToBuilder(&rolesInfoCmd, showPrivileges); + BSONObjBuilder builder; - const bool ok = - grid.catalogClient(txn)->runUserManagementReadCommand(txn, "admin", rolesInfoCmd, &builder); + const bool ok = grid.catalogClient(txn)->runUserManagementReadCommand( + txn, "admin", rolesInfoCmd.obj(), &builder); BSONObj cmdResult = builder.obj(); if (!ok) { return getStatusFromCommandResult(cmdResult); } - std::vector<BSONElement> foundRoles = cmdResult["roles"].Array(); + std::vector<BSONElement> foundRoles = cmdResult[rolesFieldName(showPrivileges)].Array(); if (foundRoles.size() == 0) { return Status(ErrorCodes::RoleNotFound, "Role \"" + roleName.toString() + "\" not found"); } @@ -155,23 +238,59 @@ Status AuthzManagerExternalStateMongos::getRoleDescription(OperationContext* txn *result = foundRoles[0].Obj().getOwned(); return Status::OK(); } +Status AuthzManagerExternalStateMongos::getRolesDescription(OperationContext* txn, + const std::vector<RoleName>& roles, + PrivilegeFormat showPrivileges, + BSONObj* result) { + BSONArrayBuilder rolesInfoCmdArray; + + for (const RoleName& roleName : roles) { + rolesInfoCmdArray << BSON(AuthorizationManager::ROLE_NAME_FIELD_NAME + << roleName.getRole() + << AuthorizationManager::ROLE_DB_FIELD_NAME + << roleName.getDB()); + } + + BSONObjBuilder rolesInfoCmd; + rolesInfoCmd.append("rolesInfo", rolesInfoCmdArray.arr()); + addShowPrivilegesToBuilder(&rolesInfoCmd, showPrivileges); + BSONObjBuilder builder; + const bool ok = grid.catalogClient(txn)->runUserManagementReadCommand( + txn, "admin", rolesInfoCmd.obj(), &builder); + BSONObj cmdResult = builder.obj(); + if (!ok) { + return getStatusFromCommandResult(cmdResult); + } + + std::vector<BSONElement> foundRoles = cmdResult[rolesFieldName(showPrivileges)].Array(); + if (foundRoles.size() == 0) { + return Status(ErrorCodes::RoleNotFound, "Roles not found"); + } + + *result = foundRoles[0].Obj().getOwned(); + + return Status::OK(); +} Status AuthzManagerExternalStateMongos::getRoleDescriptionsForDB(OperationContext* txn, const std::string dbname, - bool showPrivileges, + PrivilegeFormat showPrivileges, bool showBuiltinRoles, std::vector<BSONObj>* result) { - BSONObj rolesInfoCmd = - BSON("rolesInfo" << 1 << "showPrivileges" << showPrivileges << "showBuiltinRoles" - << showBuiltinRoles); + BSONObjBuilder rolesInfoCmd; + rolesInfoCmd << "rolesInfo" << 1 << "showBuiltinRoles" << showBuiltinRoles; + addShowPrivilegesToBuilder(&rolesInfoCmd, showPrivileges); + BSONObjBuilder builder; - const bool ok = - grid.catalogClient(txn)->runUserManagementReadCommand(txn, dbname, rolesInfoCmd, &builder); + const bool ok = grid.catalogClient(txn)->runUserManagementReadCommand( + txn, dbname, rolesInfoCmd.obj(), &builder); BSONObj cmdResult = builder.obj(); if (!ok) { return getStatusFromCommandResult(cmdResult); } - for (BSONObjIterator it(cmdResult["roles"].Obj()); it.more(); it.next()) { + + for (BSONObjIterator it(cmdResult[rolesFieldName(showPrivileges)].Obj()); it.more(); + it.next()) { result->push_back((*it).Obj().getOwned()); } return Status::OK(); diff --git a/src/mongo/db/auth/authz_manager_external_state_s.h b/src/mongo/db/auth/authz_manager_external_state_s.h index bf9ca876c43..9f9c5c12d2b 100644 --- a/src/mongo/db/auth/authz_manager_external_state_s.h +++ b/src/mongo/db/auth/authz_manager_external_state_s.h @@ -59,11 +59,15 @@ public: BSONObj* result); virtual Status getRoleDescription(OperationContext* txn, const RoleName& roleName, - bool showPrivileges, + PrivilegeFormat showPrivileges, BSONObj* result); + virtual Status getRolesDescription(OperationContext* txn, + const std::vector<RoleName>& roles, + PrivilegeFormat showPrivileges, + BSONObj* result); virtual Status getRoleDescriptionsForDB(OperationContext* txn, const std::string dbname, - bool showPrivileges, + PrivilegeFormat showPrivileges, bool showBuiltinRoles, std::vector<BSONObj>* result); diff --git a/src/mongo/db/auth/privilege_format.h b/src/mongo/db/auth/privilege_format.h new file mode 100644 index 00000000000..2f452a3ae84 --- /dev/null +++ b/src/mongo/db/auth/privilege_format.h @@ -0,0 +1,42 @@ +/** +* 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. +*/ + +#pragma once + + +namespace mongo { +/** + * How user management functions should structure the BSON representation of privileges and roles. + */ +enum class PrivilegeFormat { + kOmit, // Privileges should not be included in the BSON representation. + kShowSeparate, // Privileges should be included, each as a separate entry. + kShowAsUserFragment // Privileges and roles should all be collapsed together, and presented as + // a fragment of a user document. +}; +} // namespace mongo diff --git a/src/mongo/db/auth/sasl_authentication_session.cpp b/src/mongo/db/auth/sasl_authentication_session.cpp index c64e4be8100..fdb7f024929 100644 --- a/src/mongo/db/auth/sasl_authentication_session.cpp +++ b/src/mongo/db/auth/sasl_authentication_session.cpp @@ -69,6 +69,7 @@ bool isAuthorizedCommon(SaslAuthenticationSession* session, SaslAuthenticationSession::SaslAuthenticationSession(AuthorizationSession* authzSession) : AuthenticationSession(AuthenticationSession::SESSION_TYPE_SASL), + _txn(nullptr), _authzSession(authzSession), _saslStep(0), _conversationId(0), diff --git a/src/mongo/db/auth/user_management_commands_parser.cpp b/src/mongo/db/auth/user_management_commands_parser.cpp index 090ca7e0246..aee95bbbdef 100644 --- a/src/mongo/db/auth/user_management_commands_parser.cpp +++ b/src/mongo/db/auth/user_management_commands_parser.cpp @@ -414,10 +414,20 @@ Status parseRolesInfoCommand(const BSONObj& cmdObj, StringData dbname, RolesInfo parsedArgs->roleNames.push_back(name); } - status = bsonExtractBooleanFieldWithDefault( - cmdObj, "showPrivileges", false, &parsedArgs->showPrivileges); - if (!status.isOK()) { - return status; + BSONElement showPrivileges = cmdObj["showPrivileges"]; + if (showPrivileges.eoo()) { + parsedArgs->privilegeFormat = PrivilegeFormat::kOmit; + } else if (showPrivileges.isNumber() || showPrivileges.isBoolean()) { + parsedArgs->privilegeFormat = + showPrivileges.trueValue() ? PrivilegeFormat::kShowSeparate : PrivilegeFormat::kOmit; + } else if (showPrivileges.type() == BSONType::String && + showPrivileges.String() == "asUserFragment") { + parsedArgs->privilegeFormat = PrivilegeFormat::kShowAsUserFragment; + } else { + return Status(ErrorCodes::FailedToParse, + str::stream() << "Failed to parse 'showPrivileges'. 'showPrivileges' should " + "either be a boolean or the string 'asUserFragment', given: " + << showPrivileges.toString()); } status = bsonExtractBooleanFieldWithDefault( diff --git a/src/mongo/db/auth/user_management_commands_parser.h b/src/mongo/db/auth/user_management_commands_parser.h index 94dc3b7b2ae..720c4c1d9d7 100644 --- a/src/mongo/db/auth/user_management_commands_parser.h +++ b/src/mongo/db/auth/user_management_commands_parser.h @@ -35,6 +35,7 @@ #include "mongo/base/status.h" #include "mongo/base/string_data.h" #include "mongo/db/auth/privilege.h" +#include "mongo/db/auth/privilege_format.h" #include "mongo/db/auth/role_name.h" #include "mongo/db/auth/user.h" #include "mongo/db/auth/user_name.h" @@ -114,9 +115,10 @@ Status parseUsersInfoCommand(const BSONObj& cmdObj, StringData dbname, UsersInfo struct RolesInfoArgs { std::vector<RoleName> roleNames; bool allForDB; - bool showPrivileges; + PrivilegeFormat privilegeFormat; bool showBuiltinRoles; - RolesInfoArgs() : allForDB(false), showPrivileges(false), showBuiltinRoles(false) {} + RolesInfoArgs() + : allForDB(false), privilegeFormat(PrivilegeFormat::kOmit), showBuiltinRoles(false) {} }; /** diff --git a/src/mongo/db/commands/authentication_commands.cpp b/src/mongo/db/commands/authentication_commands.cpp index e5b50fec80d..a6a9a6f4508 100644 --- a/src/mongo/db/commands/authentication_commands.cpp +++ b/src/mongo/db/commands/authentication_commands.cpp @@ -314,7 +314,7 @@ Status CmdAuthenticate::_authenticateX509(OperationContext* txn, Client* client = Client::getCurrent(); AuthorizationSession* authorizationSession = AuthorizationSession::get(client); - auto clientName = client->session()->getX509SubjectName(); + auto clientName = client->session()->getX509PeerInfo().subjectName; if (!getSSLManager()->getSSLConfiguration().hasCA) { return Status(ErrorCodes::AuthenticationFailed, diff --git a/src/mongo/db/commands/user_management_commands.cpp b/src/mongo/db/commands/user_management_commands.cpp index 5dc5bba8138..2238383d960 100644 --- a/src/mongo/db/commands/user_management_commands.cpp +++ b/src/mongo/db/commands/user_management_commands.cpp @@ -180,7 +180,8 @@ Status checkOkayToGrantRolesToRole(OperationContext* txn, } BSONObj roleToAddDoc; - Status status = authzManager->getRoleDescription(txn, roleToAdd, false, &roleToAddDoc); + Status status = + authzManager->getRoleDescription(txn, roleToAdd, PrivilegeFormat::kOmit, &roleToAddDoc); if (status == ErrorCodes::RoleNotFound) { return Status(ErrorCodes::RoleNotFound, "Cannot grant nonexistent role " + roleToAdd.toString()); @@ -715,7 +716,8 @@ public: // Role existence has to be checked after acquiring the update lock for (size_t i = 0; i < args.roles.size(); ++i) { BSONObj ignored; - status = authzManager->getRoleDescription(txn, args.roles[i], false, &ignored); + status = authzManager->getRoleDescription( + txn, args.roles[i], PrivilegeFormat::kOmit, &ignored); if (!status.isOK()) { return appendCommandStatus(result, status); } @@ -827,7 +829,8 @@ public: if (args.hasRoles) { for (size_t i = 0; i < args.roles.size(); ++i) { BSONObj ignored; - status = authzManager->getRoleDescription(txn, args.roles[i], false, &ignored); + status = authzManager->getRoleDescription( + txn, args.roles[i], PrivilegeFormat::kOmit, &ignored); if (!status.isOK()) { return appendCommandStatus(result, status); } @@ -1035,7 +1038,8 @@ public: for (vector<RoleName>::iterator it = roles.begin(); it != roles.end(); ++it) { RoleName& roleName = *it; BSONObj roleDoc; - status = authzManager->getRoleDescription(txn, roleName, false, &roleDoc); + status = + authzManager->getRoleDescription(txn, roleName, PrivilegeFormat::kOmit, &roleDoc); if (!status.isOK()) { return appendCommandStatus(result, status); } @@ -1109,7 +1113,8 @@ public: for (vector<RoleName>::iterator it = roles.begin(); it != roles.end(); ++it) { RoleName& roleName = *it; BSONObj roleDoc; - status = authzManager->getRoleDescription(txn, roleName, false, &roleDoc); + status = + authzManager->getRoleDescription(txn, roleName, PrivilegeFormat::kOmit, &roleDoc); if (!status.isOK()) { return appendCommandStatus(result, status); } @@ -1424,7 +1429,8 @@ public: // Role existence has to be checked after acquiring the update lock BSONObj ignored; - status = authzManager->getRoleDescription(txn, args.roleName, false, &ignored); + status = + authzManager->getRoleDescription(txn, args.roleName, PrivilegeFormat::kOmit, &ignored); if (!status.isOK()) { return appendCommandStatus(result, status); } @@ -1483,6 +1489,7 @@ public: int options, string& errmsg, BSONObjBuilder& result) { + RoleName roleName; PrivilegeVector privilegesToAdd; Status status = auth::parseAndValidateRolePrivilegeManipulationCommands( @@ -1514,7 +1521,8 @@ public: } BSONObj roleDoc; - status = authzManager->getRoleDescription(txn, roleName, true, &roleDoc); + status = authzManager->getRoleDescription( + txn, roleName, PrivilegeFormat::kShowSeparate, &roleDoc); if (!status.isOK()) { return appendCommandStatus(result, status); } @@ -1616,7 +1624,8 @@ public: } BSONObj roleDoc; - status = authzManager->getRoleDescription(txn, roleName, true, &roleDoc); + status = authzManager->getRoleDescription( + txn, roleName, PrivilegeFormat::kShowSeparate, &roleDoc); if (!status.isOK()) { return appendCommandStatus(result, status); } @@ -1728,7 +1737,7 @@ public: // Role existence has to be checked after acquiring the update lock BSONObj roleDoc; - status = authzManager->getRoleDescription(txn, roleName, false, &roleDoc); + status = authzManager->getRoleDescription(txn, roleName, PrivilegeFormat::kOmit, &roleDoc); if (!status.isOK()) { return appendCommandStatus(result, status); } @@ -1818,7 +1827,7 @@ public: } BSONObj roleDoc; - status = authzManager->getRoleDescription(txn, roleName, false, &roleDoc); + status = authzManager->getRoleDescription(txn, roleName, PrivilegeFormat::kOmit, &roleDoc); if (!status.isOK()) { return appendCommandStatus(result, status); } @@ -1905,7 +1914,7 @@ public: } BSONObj roleDoc; - status = authzManager->getRoleDescription(txn, roleName, false, &roleDoc); + status = authzManager->getRoleDescription(txn, roleName, PrivilegeFormat::kOmit, &roleDoc); if (!status.isOK()) { return appendCommandStatus(result, status); } @@ -2124,6 +2133,29 @@ public: } cmdDropAllRolesFromDatabase; +/** + * Provides information about one or more roles, the indirect roles they are members of, and + * optionally the privileges they provide. + * + * This command accepts the following arguments: + * rolesInfo: + * (String) Returns information about a single role on the current database. + * {role: (String), db: (String)} Returns information about a specified role, on a specific db + * (BooleanTrue) Returns information about all roles in this database + * [ //Zero or more of + * {role: (String), db: (String) ] Returns information about all specified roles + * + * showBuiltinRoles: + * (Boolean) If true, and rolesInfo == (BooleanTrue), include built-in roles from the database + * + * showPrivileges: + * (BooleanFalse) Do not show information about privileges + * (BooleanTrue) Attach all privileges inherited from roles to role descriptions + * "asUserFragment" Render results as a partial user document as-if a user existed which possessed + * these roles. This format may change over time with changes to the auth + * schema. + */ + class CmdRolesInfo : public Command { public: virtual bool slaveOk() const { @@ -2167,33 +2199,40 @@ public: return appendCommandStatus(result, status); } - BSONArrayBuilder rolesArrayBuilder; if (args.allForDB) { std::vector<BSONObj> rolesDocs; status = getGlobalAuthorizationManager()->getRoleDescriptionsForDB( - txn, dbname, args.showPrivileges, args.showBuiltinRoles, &rolesDocs); + txn, dbname, args.privilegeFormat, args.showBuiltinRoles, &rolesDocs); if (!status.isOK()) { return appendCommandStatus(result, status); } + if (args.privilegeFormat == PrivilegeFormat::kShowAsUserFragment) { + return appendCommandStatus( + result, + Status(ErrorCodes::IllegalOperation, + "Cannot get user fragment for all roles in a database")); + } + BSONArrayBuilder rolesArrayBuilder; for (size_t i = 0; i < rolesDocs.size(); ++i) { rolesArrayBuilder.append(rolesDocs[i]); } + result.append("roles", rolesArrayBuilder.arr()); + } + + BSONObj roleDetails; + status = getGlobalAuthorizationManager()->getRolesDescription( + txn, args.roleNames, args.privilegeFormat, &roleDetails); + if (!status.isOK()) { + return appendCommandStatus(result, status); + } + + if (args.privilegeFormat == PrivilegeFormat::kShowAsUserFragment) { + result.append("userFragment", roleDetails); } else { - for (size_t i = 0; i < args.roleNames.size(); ++i) { - BSONObj roleDetails; - status = getGlobalAuthorizationManager()->getRoleDescription( - txn, args.roleNames[i], args.showPrivileges, &roleDetails); - if (status.code() == ErrorCodes::RoleNotFound) { - continue; - } - if (!status.isOK()) { - return appendCommandStatus(result, status); - } - rolesArrayBuilder.append(roleDetails); - } + result.append("roles", BSONArray(roleDetails)); } - result.append("roles", rolesArrayBuilder.arr()); + return true; } |