summaryrefslogtreecommitdiff
path: root/src/mongo/db
diff options
context:
space:
mode:
authorSpencer Jackson <spencer.jackson@mongodb.com>2016-08-08 11:51:13 -0400
committerSpencer Jackson <spencer.jackson@mongodb.com>2016-08-09 19:37:44 -0400
commitd930f4832631eca7092ada4328d780f2b8d19d31 (patch)
tree0b3920a9fd2172ed503f2ad4a1d9979a852d7fd3 /src/mongo/db
parent4c6009e67d3e503f796b5afcbcbeaa95eba80b44 (diff)
downloadmongo-d930f4832631eca7092ada4328d780f2b8d19d31.tar.gz
SERVER-22826 Support X509 Authorization
Diffstat (limited to 'src/mongo/db')
-rw-r--r--src/mongo/db/auth/SConscript17
-rw-r--r--src/mongo/db/auth/authorization_manager.cpp16
-rw-r--r--src/mongo/db/auth/authorization_manager.h45
-rw-r--r--src/mongo/db/auth/authorization_manager_test.cpp63
-rw-r--r--src/mongo/db/auth/authz_manager_external_state.cpp12
-rw-r--r--src/mongo/db/auth/authz_manager_external_state.h75
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_local.cpp81
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_local.h10
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_s.cpp211
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_s.h8
-rw-r--r--src/mongo/db/auth/privilege_format.h42
-rw-r--r--src/mongo/db/auth/sasl_authentication_session.cpp1
-rw-r--r--src/mongo/db/auth/user_management_commands_parser.cpp18
-rw-r--r--src/mongo/db/auth/user_management_commands_parser.h6
-rw-r--r--src/mongo/db/commands/authentication_commands.cpp2
-rw-r--r--src/mongo/db/commands/user_management_commands.cpp91
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;
}