summaryrefslogtreecommitdiff
path: root/src/mongo/db
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db')
-rw-r--r--src/mongo/db/auth/authorization_manager.h57
-rw-r--r--src/mongo/db/auth/authorization_manager_impl.cpp11
-rw-r--r--src/mongo/db/auth/authorization_manager_impl.h11
-rw-r--r--src/mongo/db/auth/authz_manager_external_state.h27
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_local.cpp425
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_local.h15
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_s.cpp97
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_s.h10
-rw-r--r--src/mongo/db/auth/privilege.cpp6
-rw-r--r--src/mongo/db/auth/privilege.h1
-rw-r--r--src/mongo/db/auth/role_graph.h5
-rw-r--r--src/mongo/db/auth/role_graph_builtin_roles.cpp206
-rw-r--r--src/mongo/db/commands/user_management_commands.cpp215
-rw-r--r--src/mongo/db/commands/user_management_commands_common.cpp12
14 files changed, 574 insertions, 524 deletions
diff --git a/src/mongo/db/auth/authorization_manager.h b/src/mongo/db/auth/authorization_manager.h
index 01790c106db..4769b16c25c 100644
--- a/src/mongo/db/auth/authorization_manager.h
+++ b/src/mongo/db/auth/authorization_manager.h
@@ -217,21 +217,54 @@ public:
virtual Status rolesExist(OperationContext* opCtx, const std::vector<RoleName>& roleNames) = 0;
/**
- * Delegates method call to the underlying AuthzManagerExternalState.
+ * Options for what data resolveRoles() should mine from the role tree.
+ *
+ * kRoles: Collect RoleNames in the "roles" field in each role document for subordinates.
+ * kPrivileges: Examine the "privileges" field in each role document and
+ * merge "actions" for identicate "resource" patterns.
+ * kRestrictions: Collect the "authenticationRestrictions" field in each role document.
+ *
+ * kDirectOnly: If specified, only the RoleNames explicitly supplied to resolveRoles()
+ * will be examined.
+ * If not specified, then resolveRoles() will continue examining all
+ * subordinate roles until the tree has been exhausted.
+ *
+ * kAll, kDirectRoles, kDirectPrivileges, kDirectRestrictions, and kDirectAll
+ * exist as convenience aliases for combinations of the above flags.
*/
- virtual Status getRoleDescription(OperationContext* opCtx,
- const RoleName& roleName,
- PrivilegeFormat privilegeFormat,
- AuthenticationRestrictionsFormat,
- BSONObj* result) = 0;
+ enum ResolveRoleOption : std::uint8_t {
+
+ kRoles = 0x01,
+ kPrivileges = 0x02,
+ kRestrictions = 0x04,
+ kAll = kRoles | kPrivileges | kRestrictions,
+
+ // Only collect from the first pass.
+ kDirectOnly = 0x10,
+
+ kDirectRoles = kRoles | kDirectOnly,
+ kDirectPrivileges = kPrivileges | kDirectOnly,
+ kDirectRestrictions = kRestrictions | kDirectOnly,
+ kDirectAll = kAll | kDirectOnly,
+ };
/**
- * Convenience wrapper for getRoleDescription() defaulting formats to kOmit.
+ * Return type for resolveRoles().
+ * Each member will be populated ONLY IF their corresponding Option flag was specifed.
+ * Otherwise, they will be equal to boost::none.
+ */
+ struct ResolvedRoleData {
+ boost::optional<stdx::unordered_set<RoleName>> roles;
+ boost::optional<PrivilegeVector> privileges;
+ boost::optional<RestrictionDocuments> restrictions;
+ };
+
+ /**
+ * Delegates method call to the underlying AuthzManagerExternalState.
*/
- Status getRoleDescription(OperationContext* ctx, const RoleName& roleName, BSONObj* result) {
- return getRoleDescription(
- ctx, roleName, PrivilegeFormat::kOmit, AuthenticationRestrictionsFormat::kOmit, result);
- }
+ virtual StatusWith<ResolvedRoleData> resolveRoles(OperationContext* opCtx,
+ const std::vector<RoleName>& roleNames,
+ ResolveRoleOption option) = 0;
/**
* Delegates method call to the underlying AuthzManagerExternalState.
@@ -250,7 +283,7 @@ public:
PrivilegeFormat privilegeFormat,
AuthenticationRestrictionsFormat,
bool showBuiltinRoles,
- std::vector<BSONObj>* result) = 0;
+ BSONArrayBuilder* result) = 0;
/**
* Returns a Status or UserHandle for the given userName. If the user cache already has a
diff --git a/src/mongo/db/auth/authorization_manager_impl.cpp b/src/mongo/db/auth/authorization_manager_impl.cpp
index 42439d4e791..329b7e4765f 100644
--- a/src/mongo/db/auth/authorization_manager_impl.cpp
+++ b/src/mongo/db/auth/authorization_manager_impl.cpp
@@ -421,12 +421,9 @@ Status AuthorizationManagerImpl::rolesExist(OperationContext* opCtx,
return _externalState->rolesExist(opCtx, roleNames);
}
-Status AuthorizationManagerImpl::getRoleDescription(OperationContext* opCtx,
- const RoleName& roleName,
- PrivilegeFormat privileges,
- AuthenticationRestrictionsFormat restrictions,
- BSONObj* result) {
- return _externalState->getRoleDescription(opCtx, roleName, privileges, restrictions, result);
+StatusWith<AuthorizationManager::ResolvedRoleData> AuthorizationManagerImpl::resolveRoles(
+ OperationContext* opCtx, const std::vector<RoleName>& roleNames, ResolveRoleOption option) {
+ return _externalState->resolveRoles(opCtx, roleNames, option);
}
Status AuthorizationManagerImpl::getRolesDescription(OperationContext* opCtx,
@@ -444,7 +441,7 @@ Status AuthorizationManagerImpl::getRoleDescriptionsForDB(
PrivilegeFormat privileges,
AuthenticationRestrictionsFormat restrictions,
bool showBuiltinRoles,
- std::vector<BSONObj>* result) {
+ BSONArrayBuilder* result) {
return _externalState->getRoleDescriptionsForDB(
opCtx, dbname, privileges, restrictions, showBuiltinRoles, result);
}
diff --git a/src/mongo/db/auth/authorization_manager_impl.h b/src/mongo/db/auth/authorization_manager_impl.h
index 933bbc02e32..894a18c042c 100644
--- a/src/mongo/db/auth/authorization_manager_impl.h
+++ b/src/mongo/db/auth/authorization_manager_impl.h
@@ -30,6 +30,7 @@
#pragma once
#include "mongo/db/auth/authorization_manager.h"
+#include "mongo/db/auth/privilege.h"
#include "mongo/platform/atomic_word.h"
#include "mongo/platform/mutex.h"
#include "mongo/stdx/condition_variable.h"
@@ -74,11 +75,9 @@ public:
Status rolesExist(OperationContext* opCtx, const std::vector<RoleName>& roleNames) override;
- Status getRoleDescription(OperationContext* opCtx,
- const RoleName& roleName,
- PrivilegeFormat privilegeFormat,
- AuthenticationRestrictionsFormat,
- BSONObj* result) override;
+ StatusWith<ResolvedRoleData> resolveRoles(OperationContext* opCtx,
+ const std::vector<RoleName>& roleNames,
+ ResolveRoleOption option) override;
Status getRolesDescription(OperationContext* opCtx,
const std::vector<RoleName>& roleName,
@@ -91,7 +90,7 @@ public:
PrivilegeFormat privilegeFormat,
AuthenticationRestrictionsFormat,
bool showBuiltinRoles,
- std::vector<BSONObj>* result) override;
+ BSONArrayBuilder* result) override;
StatusWith<UserHandle> acquireUser(OperationContext* opCtx, const UserName& userName) override;
StatusWith<UserHandle> acquireUserForSessionRefresh(OperationContext* opCtx,
diff --git a/src/mongo/db/auth/authz_manager_external_state.h b/src/mongo/db/auth/authz_manager_external_state.h
index 867ea493a5f..f27d577a90f 100644
--- a/src/mongo/db/auth/authz_manager_external_state.h
+++ b/src/mongo/db/auth/authz_manager_external_state.h
@@ -37,6 +37,7 @@
#include "mongo/base/status.h"
#include "mongo/db/auth/authorization_manager.h"
#include "mongo/db/auth/authorization_manager_impl.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"
@@ -104,25 +105,15 @@ public:
*/
virtual Status rolesExist(OperationContext* opCtx, const std::vector<RoleName>& roleNames) = 0;
+ using ResolveRoleOption = AuthorizationManager::ResolveRoleOption;
+ using ResolvedRoleData = AuthorizationManager::ResolvedRoleData;
+
/**
- * 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.
+ * Collects (in)direct roles, privileges, and restrictions for a set of start roles.
*/
- virtual Status getRoleDescription(OperationContext* opCtx,
- const RoleName& roleName,
- PrivilegeFormat showPrivileges,
- AuthenticationRestrictionsFormat,
- BSONObj* result) = 0;
+ virtual StatusWith<ResolvedRoleData> resolveRoles(OperationContext* opCtx,
+ const std::vector<RoleName>& roleNames,
+ ResolveRoleOption option) = 0;
/**
* Writes into "result" a document describing the named role is and returns Status::OK(). If
@@ -160,7 +151,7 @@ public:
PrivilegeFormat showPrivileges,
AuthenticationRestrictionsFormat,
bool showBuiltinRoles,
- std::vector<BSONObj>* result) = 0;
+ BSONArrayBuilder* result) = 0;
/**
* Returns true if there exists at least one privilege document in the system.
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 e530074b5a4..3d06c09c713 100644
--- a/src/mongo/db/auth/authz_manager_external_state_local.cpp
+++ b/src/mongo/db/auth/authz_manager_external_state_local.cpp
@@ -38,6 +38,7 @@
#include "mongo/bson/mutable/document.h"
#include "mongo/bson/mutable/element.h"
#include "mongo/bson/util/bson_extract.h"
+#include "mongo/db/auth/address_restriction.h"
#include "mongo/db/auth/auth_options_gen.h"
#include "mongo/db/auth/privilege_parser.h"
#include "mongo/db/auth/user_document_parser.h"
@@ -50,6 +51,7 @@
namespace mongo {
using std::vector;
+using ResolveRoleOption = AuthzManagerExternalStateLocal::ResolveRoleOption;
Status AuthzManagerExternalStateLocal::initialize(OperationContext* opCtx) {
Status status = _initializeRoleGraph(opCtx);
@@ -144,6 +146,101 @@ void addAuthenticationRestrictionObjectsToArrayElement(
fassert(40560, restrictionsElement.appendArray("", r->toBSON()));
}
}
+
+void serializeResolvedRoles(BSONObjBuilder* user,
+ const AuthzManagerExternalState::ResolvedRoleData& data,
+ const BSONObj& roleDoc) {
+ BSONArrayBuilder rolesBuilder(user->subarrayStart("inheritedRoles"));
+ for (const auto& roleName : data.roles.get()) {
+ roleName.serializeToBSON(&rolesBuilder);
+ }
+ rolesBuilder.doneFast();
+
+ if (data.privileges) {
+ BSONArrayBuilder privsBuilder(user->subarrayStart("inheritedPrivileges"));
+ for (const auto& privilege : data.privileges.get()) {
+ privsBuilder.append(privilege.toBSON());
+ }
+ privsBuilder.doneFast();
+ }
+
+ if (data.restrictions) {
+ BSONArrayBuilder arBuilder(user->subarrayStart("inheritedAuthenticationRestrictions"));
+ if (auto ar = roleDoc["authenticationRestrictions"];
+ (ar.type() == Array) && (ar.Obj().nFields() > 0)) {
+ arBuilder.append(ar);
+ }
+ if (auto ar = data.restrictions->toBSON(); ar.nFields() > 0) {
+ arBuilder.append(ar);
+ }
+ arBuilder.doneFast();
+ }
+}
+
+/**
+ * Make sure the roleDoc as retreived from storage matches expectations for options.
+ */
+constexpr auto kRolesFieldName = "roles"_sd;
+constexpr auto kPrivilegesFieldName = "privileges"_sd;
+constexpr auto kAuthenticationRestrictionFieldName = "authenticationRestrictions"_sd;
+
+std::vector<RoleName> filterAndMapRole(BSONObjBuilder* builder,
+ BSONObj role,
+ ResolveRoleOption option) {
+ std::vector<RoleName> subRoles;
+ bool sawRestrictions = false;
+
+ for (const auto& elem : role) {
+ if (elem.fieldNameStringData() == kRolesFieldName) {
+ uassert(
+ ErrorCodes::BadValue, "Invalid roles field, expected array", elem.type() == Array);
+ for (const auto& roleName : elem.Obj()) {
+ subRoles.push_back(RoleName::parseFromBSON(roleName));
+ }
+ if ((option & ResolveRoleOption::kRoles) == 0) {
+ continue;
+ }
+ }
+
+ if ((elem.fieldNameStringData() == kPrivilegesFieldName) &&
+ ((option & ResolveRoleOption::kPrivileges) == 0)) {
+ continue;
+ }
+
+ if (elem.fieldNameStringData() == kAuthenticationRestrictionFieldName) {
+ sawRestrictions = true;
+ if (option & ResolveRoleOption::kRestrictions) {
+ BSONArrayBuilder arBuilder(
+ builder->subarrayStart(kAuthenticationRestrictionFieldName));
+ arBuilder.append(elem);
+ arBuilder.doneFast();
+ }
+ continue;
+ }
+
+ builder->append(elem);
+ }
+
+ if (!sawRestrictions && (option & ResolveRoleOption::kRestrictions)) {
+ builder->append(kAuthenticationRestrictionFieldName, BSONArray());
+ }
+
+ return subRoles;
+}
+
+ResolveRoleOption makeResolveRoleOption(PrivilegeFormat showPrivileges,
+ AuthenticationRestrictionsFormat showRestrictions) {
+ auto option = ResolveRoleOption::kRoles;
+ if (showPrivileges != PrivilegeFormat::kOmit) {
+ option = static_cast<ResolveRoleOption>(option | ResolveRoleOption::kPrivileges);
+ }
+ if (showRestrictions != AuthenticationRestrictionsFormat::kOmit) {
+ option = static_cast<ResolveRoleOption>(option | ResolveRoleOption::kRestrictions);
+ }
+
+ return option;
+}
+
} // namespace
bool AuthzManagerExternalStateLocal::_checkHasAnyPrivilegeDocuments(OperationContext* opCtx) {
@@ -312,142 +409,164 @@ Status AuthzManagerExternalStateLocal::rolesExist(OperationContext* opCtx,
return Status::OK();
}
-Status AuthzManagerExternalStateLocal::getRoleDescription(
- OperationContext* opCtx,
- const RoleName& roleName,
- PrivilegeFormat showPrivileges,
- AuthenticationRestrictionsFormat showRestrictions,
- BSONObj* result) {
- if (showPrivileges == PrivilegeFormat::kShowAsUserFragment) {
- mutablebson::Document resultDoc;
- mutablebson::Element rolesElement = resultDoc.makeElementArray("roles");
- fassert(40273, resultDoc.root().pushBack(rolesElement));
- addRoleNameObjectsToArrayElement(
- rolesElement, makeRoleNameIteratorForContainer(std::vector<RoleName>{roleName}));
- resolveUserRoles(&resultDoc, {roleName});
- *result = resultDoc.getObject();
- return Status::OK();
- }
- stdx::lock_guard<Latch> lk(_roleGraphMutex);
- return _getRoleDescription_inlock(roleName, showPrivileges, showRestrictions, result);
-}
-
-Status AuthzManagerExternalStateLocal::getRolesDescription(
- OperationContext* opCtx,
- const std::vector<RoleName>& roles,
- PrivilegeFormat showPrivileges,
- AuthenticationRestrictionsFormat showRestrictions,
- BSONObj* result) {
- if (showPrivileges == PrivilegeFormat::kShowAsUserFragment) {
- mutablebson::Document resultDoc;
- mutablebson::Element rolesElement = resultDoc.makeElementArray("roles");
- fassert(40274, resultDoc.root().pushBack(rolesElement));
- addRoleNameObjectsToArrayElement(rolesElement, makeRoleNameIteratorForContainer(roles));
- resolveUserRoles(&resultDoc, roles);
- *result = resultDoc.getObject();
- return Status::OK();
- }
-
- stdx::lock_guard<Latch> lk(_roleGraphMutex);
- BSONArrayBuilder resultBuilder;
- for (const RoleName& role : roles) {
- BSONObj roleDoc;
- Status status =
- _getRoleDescription_inlock(role, showPrivileges, showRestrictions, &roleDoc);
- if (!status.isOK()) {
- if (status.code() == ErrorCodes::RoleNotFound) {
+using ResolvedRoleData = AuthzManagerExternalState::ResolvedRoleData;
+StatusWith<ResolvedRoleData> AuthzManagerExternalStateLocal::resolveRoles(
+ OperationContext* opCtx, const std::vector<RoleName>& roleNames, ResolveRoleOption option) try {
+ using RoleNameSet = typename decltype(ResolvedRoleData::roles)::value_type;
+ const bool processRoles = option & ResolveRoleOption::kRoles;
+ const bool processPrivs = option & ResolveRoleOption::kPrivileges;
+ const bool processRests = option & ResolveRoleOption::kRestrictions;
+ const bool walkIndirect = (option & ResolveRoleOption::kDirectOnly) == 0;
+
+ RoleNameSet inheritedRoles;
+ PrivilegeVector inheritedPrivileges;
+ RestrictionDocuments::sequence_type inheritedRestrictions;
+
+ RoleNameSet frontier(roleNames.cbegin(), roleNames.cend());
+ RoleNameSet visited;
+ while (!frontier.empty()) {
+ RoleNameSet nextFrontier;
+ for (const auto& role : frontier) {
+ visited.insert(role);
+
+ if (RoleGraph::isBuiltinRole(role)) {
+ if (processPrivs) {
+ RoleGraph::addPrivilegesForBuiltinRole(role, &inheritedPrivileges);
+ }
continue;
}
- return status;
- }
- resultBuilder << roleDoc;
- }
- *result = resultBuilder.arr();
- return Status::OK();
-}
-
-Status AuthzManagerExternalStateLocal::_getRoleDescription_inlock(
- const RoleName& roleName,
- PrivilegeFormat showPrivileges,
- AuthenticationRestrictionsFormat showRestrictions,
- BSONObj* result) {
- if (!_roleGraph.roleExists(roleName))
- return Status(ErrorCodes::RoleNotFound, "No role named " + roleName.toString());
-
- mutablebson::Document resultDoc;
- fassert(17162,
- resultDoc.root().appendString(AuthorizationManager::ROLE_NAME_FIELD_NAME,
- roleName.getRole()));
- fassert(
- 17163,
- resultDoc.root().appendString(AuthorizationManager::ROLE_DB_FIELD_NAME, roleName.getDB()));
- fassert(17267, resultDoc.root().appendBool("isBuiltin", _roleGraph.isBuiltinRole(roleName)));
- auto warningsElement = resultDoc.makeElementArray("warnings");
+ BSONObj roleDoc;
+ auto status = findOne(
+ opCtx, AuthorizationManager::rolesCollectionNamespace, role.toBSON(), &roleDoc);
+ if (!status.isOK()) {
+ if (status.code() == ErrorCodes::NoMatchingDocument) {
+ return {ErrorCodes::RoleNotFound,
+ str::stream() << "Role '" << role.getFullName() << "' does not exist"};
+ }
+ return status;
+ }
- auto rolesElement = resultDoc.makeElementArray("roles");
- fassert(17164, resultDoc.root().pushBack(rolesElement));
- addRoleNameObjectsToArrayElement(rolesElement, _roleGraph.getDirectSubordinates(roleName));
+ BSONElement elem;
+ if ((processRoles || walkIndirect) && (elem = roleDoc["roles"])) {
+ if (elem.type() != Array) {
+ return {ErrorCodes::BadValue,
+ str::stream()
+ << "Invalid 'roles' field in role document '" << role.getFullName()
+ << "', expected an array but found " << typeName(elem.type())};
+ }
+ for (const auto& subroleElem : elem.Obj()) {
+ auto subrole = RoleName::parseFromBSON(subroleElem);
+ if (visited.count(subrole) || nextFrontier.count(subrole)) {
+ continue;
+ }
+ if (walkIndirect) {
+ nextFrontier.insert(subrole);
+ }
+ if (processRoles) {
+ inheritedRoles.insert(std::move(subrole));
+ }
+ }
+ }
- auto inheritedRolesElement = resultDoc.makeElementArray("inheritedRoles");
- fassert(17165, resultDoc.root().pushBack(inheritedRolesElement));
+ if (processPrivs && (elem = roleDoc["privileges"])) {
+ if (elem.type() != Array) {
+ return {ErrorCodes::BadValue,
+ str::stream() << "Invalid 'privileges' field in role document '"
+ << role.getFullName() << "'"};
+ }
+ for (const auto& privElem : elem.Obj()) {
+ auto priv = Privilege::fromBSON(privElem);
+ Privilege::addPrivilegeToPrivilegeVector(&inheritedPrivileges, priv);
+ }
+ }
- auto privilegesElement = resultDoc.makeElementArray("privileges");
- if (showPrivileges == PrivilegeFormat::kShowSeparate) {
- fassert(17166, resultDoc.root().pushBack(privilegesElement));
+ if (processRests && (elem = roleDoc["authenticationRestrictions"])) {
+ if (elem.type() != Array) {
+ return {ErrorCodes::BadValue,
+ str::stream()
+ << "Invalid 'authenticationRestrictions' field in role document '"
+ << role.getFullName() << "'"};
+ }
+ inheritedRestrictions.push_back(
+ uassertStatusOK(parseAuthenticationRestriction(BSONArray(elem.Obj()))));
+ }
+ }
+ frontier = std::move(nextFrontier);
}
- if (showRestrictions == AuthenticationRestrictionsFormat::kShow) {
- auto authenticationRestrictionsElement =
- resultDoc.makeElementArray("authenticationRestrictions");
- fassert(40559, resultDoc.root().pushBack(authenticationRestrictionsElement));
-
- const auto& restrictions = _roleGraph.getDirectAuthenticationRestrictions(roleName);
- if (restrictions.get()) {
- fassert(40561,
- authenticationRestrictionsElement.appendArray("", restrictions->toBSON()));
- }
+ ResolvedRoleData ret;
+ if (processRoles) {
+ ret.roles = std::move(inheritedRoles);
+ }
+ if (processPrivs) {
+ ret.privileges = std::move(inheritedPrivileges);
+ }
+ if (processRests) {
+ ret.restrictions = RestrictionDocuments(std::move(inheritedRestrictions));
}
- if (_roleGraphState == roleGraphStateConsistent) {
- addRoleNameObjectsToArrayElement(inheritedRolesElement,
- _roleGraph.getIndirectSubordinates(roleName));
+ return ret;
+} catch (const AssertionException& ex) {
+ return ex.toStatus();
+}
- if (showPrivileges == PrivilegeFormat::kShowSeparate) {
- auto inheritedPrivilegesElement = resultDoc.makeElementArray("inheritedPrivileges");
- addPrivilegeObjectsOrWarningsToArrayElement(
- privilegesElement, warningsElement, _roleGraph.getDirectPrivileges(roleName));
+Status AuthzManagerExternalStateLocal::getRolesDescription(
+ OperationContext* opCtx,
+ const std::vector<RoleName>& roleNames,
+ PrivilegeFormat showPrivileges,
+ AuthenticationRestrictionsFormat showRestrictions,
+ BSONObj* result) {
+ auto option = makeResolveRoleOption(showPrivileges, showRestrictions);
- addPrivilegeObjectsOrWarningsToArrayElement(
- inheritedPrivilegesElement, warningsElement, _roleGraph.getAllPrivileges(roleName));
+ if (showPrivileges == PrivilegeFormat::kShowAsUserFragment) {
+ BSONObjBuilder fragment;
- fassert(17323, resultDoc.root().pushBack(inheritedPrivilegesElement));
+ BSONArrayBuilder rolesBuilder(fragment.subarrayStart("roles"));
+ for (const auto& roleName : roleNames) {
+ roleName.serializeToBSON(&rolesBuilder);
}
+ rolesBuilder.doneFast();
- if (showRestrictions == AuthenticationRestrictionsFormat::kShow) {
- auto inheritedAuthenticationRestrictionsElement =
- resultDoc.makeElementArray("inheritedAuthenticationRestrictions");
- fassert(40563, resultDoc.root().pushBack(inheritedAuthenticationRestrictionsElement));
+ auto swData = resolveRoles(opCtx, roleNames, option);
+ if (!swData.isOK()) {
+ return swData.getStatus();
+ }
+ auto data = std::move(swData.getValue());
+ data.roles->insert(roleNames.cbegin(), roleNames.cend());
+ serializeResolvedRoles(&fragment, data, BSONObj());
+ *result = fragment.obj();
+ return Status::OK();
+ }
- for (const auto& restrictions : _roleGraph.getAllAuthenticationRestrictions(roleName)) {
- fassert(40562,
- inheritedAuthenticationRestrictionsElement.appendArray(
- "", restrictions->toBSON()));
+ BSONArrayBuilder rolesBuilder;
+ for (const RoleName& role : roleNames) {
+ try {
+ BSONObj roleDoc;
+ auto status = findOne(
+ opCtx, AuthorizationManager::rolesCollectionNamespace, role.toBSON(), &roleDoc);
+ if (!status.isOK()) {
+ if (status.code() == ErrorCodes::NoMatchingDocument) {
+ continue;
+ }
+ uassertStatusOK(status); // throws
}
+
+ BSONObjBuilder roleBuilder(rolesBuilder.subobjStart());
+ auto subRoles = filterAndMapRole(&roleBuilder, roleDoc, option);
+ auto data = uassertStatusOK(resolveRoles(opCtx, subRoles, option));
+ data.roles->insert(subRoles.cbegin(), subRoles.cend());
+ serializeResolvedRoles(&roleBuilder, data, roleDoc);
+
+ roleBuilder.doneFast();
+ } catch (const AssertionException& ex) {
+ return {ex.code(),
+ str::stream() << "Failed fetching role '" << role.getFullName()
+ << "': " << ex.reason()};
}
- } else if (showPrivileges == PrivilegeFormat::kShowSeparate) {
- addPrivilegeObjectsOrWarningsToArrayElement(
- privilegesElement, warningsElement, _roleGraph.getDirectPrivileges(roleName));
- fassert(40557,
- warningsElement.appendString("",
- "Role graph state inconsistent; only direct "
- "privileges and restrictions available."));
}
- if (warningsElement.hasChildren()) {
- fassert(17167, resultDoc.root().pushBack(warningsElement));
- }
- *result = resultDoc.getObject();
+ *result = rolesBuilder.arr();
return Status::OK();
}
@@ -457,26 +576,70 @@ Status AuthzManagerExternalStateLocal::getRoleDescriptionsForDB(
PrivilegeFormat showPrivileges,
AuthenticationRestrictionsFormat showRestrictions,
bool showBuiltinRoles,
- vector<BSONObj>* result) {
+ BSONArrayBuilder* result) {
+ auto option = makeResolveRoleOption(showPrivileges, showRestrictions);
+
if (showPrivileges == PrivilegeFormat::kShowAsUserFragment) {
- return Status(ErrorCodes::IllegalOperation,
- "Cannot get user fragment for all roles in a database");
+ return {ErrorCodes::IllegalOperation,
+ "Cannot get user fragment for all roles in a database"};
}
- stdx::lock_guard<Latch> lk(_roleGraphMutex);
- for (RoleNameIterator it = _roleGraph.getRolesForDatabase(dbname); it.more(); it.next()) {
- if (!showBuiltinRoles && _roleGraph.isBuiltinRole(it.get())) {
- continue;
- }
- BSONObj roleDoc;
- Status status =
- _getRoleDescription_inlock(it.get(), showPrivileges, showRestrictions, &roleDoc);
- if (!status.isOK()) {
- return status;
+ if (showBuiltinRoles) {
+ for (const auto& roleName : RoleGraph::getBuiltinRoleNamesForDB(dbname)) {
+ BSONObjBuilder roleBuilder(result->subobjStart());
+
+ roleBuilder.append(AuthorizationManager::ROLE_NAME_FIELD_NAME, roleName.getRole());
+ roleBuilder.append(AuthorizationManager::ROLE_DB_FIELD_NAME, roleName.getDB());
+ roleBuilder.append("isBuiltin", true);
+
+ roleBuilder.append("roles", BSONArray());
+ roleBuilder.append("inheritedRoles", BSONArray());
+
+ if (showPrivileges == PrivilegeFormat::kShowSeparate) {
+ BSONArrayBuilder privsBuilder(roleBuilder.subarrayStart("privileges"));
+ PrivilegeVector privs;
+ RoleGraph::addPrivilegesForBuiltinRole(roleName, &privs);
+ for (const auto& privilege : privs) {
+ privsBuilder.append(privilege.toBSON());
+ }
+ privsBuilder.doneFast();
+
+ // Builtin roles have identival privs/inheritedPrivs
+ BSONArrayBuilder ipBuilder(roleBuilder.subarrayStart("inheritedPrivileges"));
+ for (const auto& privilege : privs) {
+ ipBuilder.append(privilege.toBSON());
+ }
+ ipBuilder.doneFast();
+ }
+
+ if (showRestrictions == AuthenticationRestrictionsFormat::kShow) {
+ roleBuilder.append("authenticationRestrictions", BSONArray());
+ roleBuilder.append("inheritedAuthenticationRestrictions", BSONArray());
+ }
+
+ roleBuilder.doneFast();
}
- result->push_back(roleDoc);
}
- return Status::OK();
+
+ return query(opCtx,
+ AuthorizationManager::rolesCollectionNamespace,
+ BSON(AuthorizationManager::ROLE_DB_FIELD_NAME << dbname),
+ BSONObj(),
+ [&](const BSONObj& roleDoc) {
+ try {
+ BSONObjBuilder roleBuilder(result->subobjStart());
+
+ auto subRoles = filterAndMapRole(&roleBuilder, roleDoc, option);
+ roleBuilder.append("isBuiltin", false);
+ auto data = uassertStatusOK(resolveRoles(opCtx, subRoles, option));
+ data.roles->insert(subRoles.cbegin(), subRoles.cend());
+ serializeResolvedRoles(&roleBuilder, data, roleDoc);
+ roleBuilder.doneFast();
+ return Status::OK();
+ } catch (const AssertionException& ex) {
+ return ex.toStatus();
+ }
+ });
}
namespace {
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 0832d9ea4fa..3e434c305b5 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,9 @@ public:
const UserRequest& user,
BSONObj* result) override;
Status rolesExist(OperationContext* opCtx, const std::vector<RoleName>& roleNames) override;
- Status getRoleDescription(OperationContext* opCtx,
- const RoleName& roleName,
- PrivilegeFormat showPrivileges,
- AuthenticationRestrictionsFormat,
- BSONObj* result) override;
+ StatusWith<ResolvedRoleData> resolveRoles(OperationContext* opCtx,
+ const std::vector<RoleName>& roleNames,
+ ResolveRoleOption option) override;
Status getRolesDescription(OperationContext* opCtx,
const std::vector<RoleName>& roles,
PrivilegeFormat showPrivileges,
@@ -78,7 +76,7 @@ public:
PrivilegeFormat showPrivileges,
AuthenticationRestrictionsFormat,
bool showBuiltinRoles,
- std::vector<BSONObj>* result) override;
+ BSONArrayBuilder* result) override;
bool hasAnyPrivilegeDocuments(OperationContext* opCtx) final {
return _hasAnyPrivilegeDocuments.load();
@@ -158,11 +156,6 @@ private:
*/
Status _getUserDocument(OperationContext* opCtx, const UserName& userName, BSONObj* result);
- Status _getRoleDescription_inlock(const RoleName& roleName,
- PrivilegeFormat showPrivileges,
- AuthenticationRestrictionsFormat showRestrictions,
- BSONObj* result);
-
/**
* Returns true if the auth DB contains any users or roles.
*/
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 d43be5722ce..0d521a66b36 100644
--- a/src/mongo/db/auth/authz_manager_external_state_s.cpp
+++ b/src/mongo/db/auth/authz_manager_external_state_s.cpp
@@ -243,75 +243,25 @@ Status AuthzManagerExternalStateMongos::rolesExist(OperationContext* opCtx,
return ex.toStatus();
}
-Status AuthzManagerExternalStateMongos::getRoleDescription(
- OperationContext* opCtx,
- const RoleName& roleName,
- PrivilegeFormat showPrivileges,
- AuthenticationRestrictionsFormat showRestrictions,
- BSONObj* result) {
- BSONObjBuilder rolesInfoCmd;
- rolesInfoCmd.append(
- "rolesInfo",
- BSON_ARRAY(BSON(AuthorizationManager::ROLE_NAME_FIELD_NAME
- << roleName.getRole() << AuthorizationManager::ROLE_DB_FIELD_NAME
- << roleName.getDB())));
- addShowToBuilder(&rolesInfoCmd, showPrivileges, showRestrictions);
-
- BSONObjBuilder builder;
- const bool ok = Grid::get(opCtx)->catalogClient()->runUserManagementReadCommand(
- opCtx, "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, "Role \"" + roleName.toString() + "\" not found");
- }
-
- if (foundRoles.size() > 1) {
- return Status(ErrorCodes::RoleDataInconsistent,
- str::stream() << "Found multiple roles on the \"" << roleName.getDB()
- << "\" database with name \"" << roleName.getRole() << "\"");
- }
- *result = foundRoles[0].Obj().getOwned();
- return Status::OK();
+using ResolvedRoleData = AuthzManagerExternalState::ResolvedRoleData;
+StatusWith<ResolvedRoleData> AuthzManagerExternalStateMongos::resolveRoles(
+ OperationContext* opCtx, const std::vector<RoleName>& roleNames, ResolveRoleOption option) {
+ // mongos never calls into resolveRoles().
+ // That's done exclusively by mongod's User acquisition and user management commands.
+ dassert(false);
+ return {ErrorCodes::NotImplemented, "AuthzManagerExternalStateMongos::resolveRoles"};
}
+
Status AuthzManagerExternalStateMongos::getRolesDescription(
OperationContext* opCtx,
const std::vector<RoleName>& roles,
PrivilegeFormat showPrivileges,
AuthenticationRestrictionsFormat showRestrictions,
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());
- addShowToBuilder(&rolesInfoCmd, showPrivileges, showRestrictions);
-
- BSONObjBuilder builder;
- const bool ok = Grid::get(opCtx)->catalogClient()->runUserManagementReadCommand(
- opCtx, "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();
+ // mongos never calls into resolveRoles().
+ // That's done exclusively by mongod's User acquisition and user management commands.
+ dassert(false);
+ return {ErrorCodes::NotImplemented, "AuthzManagerExternalStateMongos::resolveRoles"};
}
Status AuthzManagerExternalStateMongos::getRoleDescriptionsForDB(
OperationContext* opCtx,
@@ -319,24 +269,11 @@ Status AuthzManagerExternalStateMongos::getRoleDescriptionsForDB(
PrivilegeFormat showPrivileges,
AuthenticationRestrictionsFormat showRestrictions,
bool showBuiltinRoles,
- std::vector<BSONObj>* result) {
- BSONObjBuilder rolesInfoCmd;
- rolesInfoCmd << "rolesInfo" << 1 << "showBuiltinRoles" << showBuiltinRoles;
- addShowToBuilder(&rolesInfoCmd, showPrivileges, showRestrictions);
-
- BSONObjBuilder builder;
- const bool ok = Grid::get(opCtx)->catalogClient()->runUserManagementReadCommand(
- opCtx, dbname.toString(), rolesInfoCmd.obj(), &builder);
- BSONObj cmdResult = builder.obj();
- if (!ok) {
- return getStatusFromCommandResult(cmdResult);
- }
-
- for (BSONObjIterator it(cmdResult[rolesFieldName(showPrivileges)].Obj()); it.more();
- it.next()) {
- result->push_back((*it).Obj().getOwned());
- }
- return Status::OK();
+ BSONArrayBuilder* result) {
+ // mongos never calls into resolveRoles().
+ // That's done exclusively by mongod's User acquisition and user management commands.
+ dassert(false);
+ return {ErrorCodes::NotImplemented, "AuthzManagerExternalStateMongos::resolveRoles"};
}
bool AuthzManagerExternalStateMongos::hasAnyPrivilegeDocuments(OperationContext* opCtx) {
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 4d310c6aa47..00616c2ab26 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,9 @@ public:
Status getUserDescription(OperationContext* opCtx,
const UserRequest& user,
BSONObj* result) final;
- Status getRoleDescription(OperationContext* opCtx,
- const RoleName& roleName,
- PrivilegeFormat showPrivileges,
- AuthenticationRestrictionsFormat,
- BSONObj* result) final;
+ StatusWith<ResolvedRoleData> resolveRoles(OperationContext* opCtx,
+ const std::vector<RoleName>& roleNames,
+ ResolveRoleOption option) override;
Status getRolesDescription(OperationContext* opCtx,
const std::vector<RoleName>& roles,
PrivilegeFormat showPrivileges,
@@ -74,7 +72,7 @@ public:
PrivilegeFormat showPrivileges,
AuthenticationRestrictionsFormat,
bool showBuiltinRoles,
- std::vector<BSONObj>* result) final;
+ BSONArrayBuilder* result) final;
bool hasAnyPrivilegeDocuments(OperationContext* opCtx) final;
};
diff --git a/src/mongo/db/auth/privilege.cpp b/src/mongo/db/auth/privilege.cpp
index 033ec7cbb08..032124b1a52 100644
--- a/src/mongo/db/auth/privilege.cpp
+++ b/src/mongo/db/auth/privilege.cpp
@@ -84,6 +84,12 @@ BSONObj Privilege::toBSON() const {
return pp.toBSON();
}
+Privilege Privilege::fromBSON(const BSONElement elem) {
+ uassert(
+ ErrorCodes::BadValue, "Privilege documents must be of type object", elem.type() == Object);
+ return fromBSON(elem.Obj());
+}
+
Privilege Privilege::fromBSON(BSONObj obj) {
ParsedPrivilege pp;
std::string errmsg;
diff --git a/src/mongo/db/auth/privilege.h b/src/mongo/db/auth/privilege.h
index f610e8adf94..633b6fce74c 100644
--- a/src/mongo/db/auth/privilege.h
+++ b/src/mongo/db/auth/privilege.h
@@ -87,6 +87,7 @@ public:
// Checks if the given actions are present in the Privilege.
bool includesActions(const ActionSet& actions) const;
+ static Privilege fromBSON(const BSONElement obj);
static Privilege fromBSON(BSONObj obj);
BSONObj toBSON() const;
diff --git a/src/mongo/db/auth/role_graph.h b/src/mongo/db/auth/role_graph.h
index 52343322b68..4d68d47e3ea 100644
--- a/src/mongo/db/auth/role_graph.h
+++ b/src/mongo/db/auth/role_graph.h
@@ -73,6 +73,11 @@ public:
*/
static bool addPrivilegesForBuiltinRole(const RoleName& role, PrivilegeVector* privileges);
+ /**
+ * Ennumerate all builtin RoleNames for the given database.
+ */
+ static stdx::unordered_set<RoleName> getBuiltinRoleNamesForDB(StringData dbname);
+
// Swaps the contents of this RoleGraph with those of "other"
void swap(RoleGraph& other);
diff --git a/src/mongo/db/auth/role_graph_builtin_roles.cpp b/src/mongo/db/auth/role_graph_builtin_roles.cpp
index 9182cd4a997..e677dd68e32 100644
--- a/src/mongo/db/auth/role_graph_builtin_roles.cpp
+++ b/src/mongo/db/auth/role_graph_builtin_roles.cpp
@@ -664,54 +664,96 @@ void addInternalRolePrivileges(PrivilegeVector* privileges) {
RoleGraph::generateUniversalPrivileges(privileges);
}
+class BuiltinRoleDefinition {
+public:
+ BuiltinRoleDefinition() = delete;
+
+ using AddPrivilegesFn = void (*)(PrivilegeVector*);
+ BuiltinRoleDefinition(bool adminOnly, AddPrivilegesFn fn)
+ : _adminOnly(adminOnly), _addPrivileges(fn) {}
+
+ using AddPrivilegesWithDBFn = void (*)(PrivilegeVector*, StringData);
+ BuiltinRoleDefinition(bool adminOnly, AddPrivilegesWithDBFn fn)
+ : _adminOnly(adminOnly), _addPrivilegesWithDB(fn) {}
+
+ bool adminOnly() const {
+ return _adminOnly;
+ }
+
+ void operator()(PrivilegeVector* result, StringData dbname) const {
+ if (_addPrivileges) {
+ dassert(!_addPrivilegesWithDB);
+ _addPrivileges(result);
+ } else {
+ dassert(_addPrivilegesWithDB);
+ _addPrivilegesWithDB(result, dbname);
+ }
+ }
+
+private:
+ bool _adminOnly;
+ AddPrivilegesFn _addPrivileges = nullptr;
+ AddPrivilegesWithDBFn _addPrivilegesWithDB = nullptr;
+};
+
+const std::map<StringData, BuiltinRoleDefinition> kBuiltinRoles({
+ // All DBs.
+ {BUILTIN_ROLE_READ, {false, addReadOnlyDbPrivileges}},
+ {BUILTIN_ROLE_READ_WRITE, {false, addReadWriteDbPrivileges}},
+ {BUILTIN_ROLE_USER_ADMIN, {false, addUserAdminDbPrivileges}},
+ {BUILTIN_ROLE_DB_ADMIN, {false, addDbAdminDbPrivileges}},
+ {BUILTIN_ROLE_DB_OWNER, {false, addDbOwnerPrivileges}},
+ {BUILTIN_ROLE_ENABLE_SHARDING, {false, addEnableShardingPrivileges}},
+ // Admin Only.
+ {BUILTIN_ROLE_READ_ANY_DB, {true, addReadOnlyAnyDbPrivileges}},
+ {BUILTIN_ROLE_READ_WRITE_ANY_DB, {true, addReadWriteAnyDbPrivileges}},
+ {BUILTIN_ROLE_USER_ADMIN_ANY_DB, {true, addUserAdminAnyDbPrivileges}},
+ {BUILTIN_ROLE_DB_ADMIN_ANY_DB, {true, addDbAdminAnyDbPrivileges}},
+ {BUILTIN_ROLE_CLUSTER_MONITOR, {true, addClusterMonitorPrivileges}},
+ {BUILTIN_ROLE_HOST_MANAGEMENT, {true, addHostManagerPrivileges}},
+ {BUILTIN_ROLE_CLUSTER_MANAGEMENT, {true, addClusterManagerPrivileges}},
+ {BUILTIN_ROLE_CLUSTER_ADMIN, {true, addClusterAdminPrivileges}},
+ {BUILTIN_ROLE_QUERYABLE_BACKUP, {true, addQueryableBackupPrivileges}},
+ {BUILTIN_ROLE_BACKUP, {true, addBackupPrivileges}},
+ {BUILTIN_ROLE_RESTORE, {true, addRestorePrivileges}},
+ {BUILTIN_ROLE_ROOT, {true, addRootRolePrivileges}},
+ {BUILTIN_ROLE_INTERNAL, {true, addInternalRolePrivileges}},
+});
+
} // namespace
+stdx::unordered_set<RoleName> RoleGraph::getBuiltinRoleNamesForDB(StringData dbname) {
+ const bool isAdmin = dbname == ADMIN_DBNAME;
+
+ stdx::unordered_set<RoleName> roleNames;
+ for (const auto& [role, def] : kBuiltinRoles) {
+ if (isAdmin || !def.adminOnly()) {
+ roleNames.insert(RoleName(role, dbname));
+ }
+ }
+ return roleNames;
+}
+
bool RoleGraph::addPrivilegesForBuiltinRole(const RoleName& roleName, PrivilegeVector* result) {
- const bool isAdminDB = (roleName.getDB() == ADMIN_DBNAME);
-
- if (roleName.getRole() == BUILTIN_ROLE_READ) {
- addReadOnlyDbPrivileges(result, roleName.getDB());
- } else if (roleName.getRole() == BUILTIN_ROLE_READ_WRITE) {
- addReadWriteDbPrivileges(result, roleName.getDB());
- } else if (roleName.getRole() == BUILTIN_ROLE_USER_ADMIN) {
- addUserAdminDbPrivileges(result, roleName.getDB());
- } else if (roleName.getRole() == BUILTIN_ROLE_DB_ADMIN) {
- addDbAdminDbPrivileges(result, roleName.getDB());
- } else if (roleName.getRole() == BUILTIN_ROLE_DB_OWNER) {
- addDbOwnerPrivileges(result, roleName.getDB());
- } else if (roleName.getRole() == BUILTIN_ROLE_ENABLE_SHARDING) {
- addEnableShardingPrivileges(result);
- } else if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_READ_ANY_DB) {
- addReadOnlyAnyDbPrivileges(result);
- } else if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_READ_WRITE_ANY_DB) {
- addReadWriteAnyDbPrivileges(result);
- } else if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_USER_ADMIN_ANY_DB) {
- addUserAdminAnyDbPrivileges(result);
- } else if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_DB_ADMIN_ANY_DB) {
- addDbAdminAnyDbPrivileges(result);
- } else if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_CLUSTER_MONITOR) {
- addClusterMonitorPrivileges(result);
- } else if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_HOST_MANAGEMENT) {
- addHostManagerPrivileges(result);
- } else if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_CLUSTER_MANAGEMENT) {
- addClusterManagerPrivileges(result);
- } else if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_CLUSTER_ADMIN) {
- addClusterAdminPrivileges(result);
- } else if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_QUERYABLE_BACKUP) {
- addQueryableBackupPrivileges(result);
- } else if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_BACKUP) {
- addBackupPrivileges(result);
- } else if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_RESTORE) {
- addRestorePrivileges(result);
- } else if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_ROOT) {
- addRootRolePrivileges(result);
- } else if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_INTERNAL) {
- addInternalRolePrivileges(result);
- } else {
+ auto role = roleName.getRole();
+ auto dbname = roleName.getDB();
+
+ if (!NamespaceString::validDBName(dbname, NamespaceString::DollarInDbNameBehavior::Allow) ||
+ dbname == "$external") {
+ return false;
+ }
+
+ auto it = kBuiltinRoles.find(role);
+ if (it == kBuiltinRoles.end()) {
return false;
}
+ const auto& def = it->second;
- // One of the roles has matched, otherwise we would have returned already.
+ if (def.adminOnly() && (dbname != ADMIN_DBNAME)) {
+ return false;
+ }
+
+ def(result, dbname);
return true;
}
@@ -722,78 +764,28 @@ void RoleGraph::generateUniversalPrivileges(PrivilegeVector* privileges) {
}
bool RoleGraph::isBuiltinRole(const RoleName& role) {
- if (!NamespaceString::validDBName(role.getDB(),
- NamespaceString::DollarInDbNameBehavior::Allow) ||
- role.getDB() == "$external") {
+ auto dbname = role.getDB();
+ if (!NamespaceString::validDBName(dbname, NamespaceString::DollarInDbNameBehavior::Allow) ||
+ dbname == "$external") {
return false;
}
- bool isAdminDB = role.getDB() == ADMIN_DBNAME;
-
- if (role.getRole() == BUILTIN_ROLE_READ) {
- return true;
- } else if (role.getRole() == BUILTIN_ROLE_READ_WRITE) {
- return true;
- } else if (role.getRole() == BUILTIN_ROLE_USER_ADMIN) {
- return true;
- } else if (role.getRole() == BUILTIN_ROLE_DB_ADMIN) {
- return true;
- } else if (role.getRole() == BUILTIN_ROLE_DB_OWNER) {
- return true;
- } else if (role.getRole() == BUILTIN_ROLE_ENABLE_SHARDING) {
- return true;
- } else if (isAdminDB && role.getRole() == BUILTIN_ROLE_READ_ANY_DB) {
- return true;
- } else if (isAdminDB && role.getRole() == BUILTIN_ROLE_READ_WRITE_ANY_DB) {
- return true;
- } else if (isAdminDB && role.getRole() == BUILTIN_ROLE_USER_ADMIN_ANY_DB) {
- return true;
- } else if (isAdminDB && role.getRole() == BUILTIN_ROLE_DB_ADMIN_ANY_DB) {
- return true;
- } else if (isAdminDB && role.getRole() == BUILTIN_ROLE_CLUSTER_MONITOR) {
- return true;
- } else if (isAdminDB && role.getRole() == BUILTIN_ROLE_HOST_MANAGEMENT) {
- return true;
- } else if (isAdminDB && role.getRole() == BUILTIN_ROLE_CLUSTER_MANAGEMENT) {
- return true;
- } else if (isAdminDB && role.getRole() == BUILTIN_ROLE_CLUSTER_ADMIN) {
- return true;
- } else if (isAdminDB && role.getRole() == BUILTIN_ROLE_BACKUP) {
- return true;
- } else if (isAdminDB && role.getRole() == BUILTIN_ROLE_RESTORE) {
- return true;
- } else if (isAdminDB && role.getRole() == BUILTIN_ROLE_ROOT) {
- return true;
- } else if (isAdminDB && role.getRole() == BUILTIN_ROLE_INTERNAL) {
- return true;
- } else if (isAdminDB && role.getRole() == BUILTIN_ROLE_QUERYABLE_BACKUP) {
- return true;
+ const auto it = kBuiltinRoles.find(role.getRole());
+ if (it == kBuiltinRoles.end()) {
+ return false;
}
- return false;
+
+ return !it->second.adminOnly() || (dbname == ADMIN_DBNAME);
}
void RoleGraph::_createBuiltinRolesForDBIfNeeded(StringData dbname) {
- _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_READ, dbname));
- _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_READ_WRITE, dbname));
- _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_USER_ADMIN, dbname));
- _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_DB_ADMIN, dbname));
- _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_DB_OWNER, dbname));
- _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_ENABLE_SHARDING, dbname));
-
- if (dbname == "admin") {
- _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_READ_ANY_DB, dbname));
- _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_READ_WRITE_ANY_DB, dbname));
- _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_USER_ADMIN_ANY_DB, dbname));
- _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_DB_ADMIN_ANY_DB, dbname));
- _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_CLUSTER_MONITOR, dbname));
- _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_HOST_MANAGEMENT, dbname));
- _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_CLUSTER_MANAGEMENT, dbname));
- _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_CLUSTER_ADMIN, dbname));
- _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_BACKUP, dbname));
- _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_RESTORE, dbname));
- _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_ROOT, dbname));
- _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_INTERNAL, dbname));
- _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_QUERYABLE_BACKUP, dbname));
+ const bool isAdmin = dbname == ADMIN_DBNAME;
+
+ for (const auto& [rolename, def] : kBuiltinRoles) {
+ if (def.adminOnly() && !isAdmin) {
+ continue;
+ }
+ _createBuiltinRoleIfNeeded(RoleName(rolename, dbname));
}
}
diff --git a/src/mongo/db/commands/user_management_commands.cpp b/src/mongo/db/commands/user_management_commands.cpp
index 47f5bc48ee7..15b40b7ddc6 100644
--- a/src/mongo/db/commands/user_management_commands.cpp
+++ b/src/mongo/db/commands/user_management_commands.cpp
@@ -88,45 +88,21 @@ Status useDefaultCode(const Status& status, ErrorCodes::Error defaultCode) {
return Status(defaultCode, status.reason());
}
-BSONArray roleSetToBSONArray(const stdx::unordered_set<RoleName>& roles) {
- BSONArrayBuilder rolesArrayBuilder;
- for (stdx::unordered_set<RoleName>::const_iterator it = roles.begin(); it != roles.end();
- ++it) {
- const RoleName& role = *it;
- rolesArrayBuilder.append(BSON(AuthorizationManager::ROLE_NAME_FIELD_NAME
- << role.getRole() << AuthorizationManager::ROLE_DB_FIELD_NAME
- << role.getDB()));
- }
- return rolesArrayBuilder.arr();
-}
-
-BSONArray rolesVectorToBSONArray(const std::vector<RoleName>& roles) {
- BSONArrayBuilder rolesArrayBuilder;
- for (std::vector<RoleName>::const_iterator it = roles.begin(); it != roles.end(); ++it) {
- const RoleName& role = *it;
- rolesArrayBuilder.append(BSON(AuthorizationManager::ROLE_NAME_FIELD_NAME
- << role.getRole() << AuthorizationManager::ROLE_DB_FIELD_NAME
- << role.getDB()));
+template <typename Container>
+BSONArray containerToBSONArray(const Container& container) {
+ BSONArrayBuilder arrayBuilder;
+ for (const auto& item : container) {
+ arrayBuilder.append(item.toBSON());
}
- return rolesArrayBuilder.arr();
+ return arrayBuilder.arr();
}
Status privilegeVectorToBSONArray(const PrivilegeVector& privileges, BSONArray* result) {
- BSONArrayBuilder arrBuilder;
- for (PrivilegeVector::const_iterator it = privileges.begin(); it != privileges.end(); ++it) {
- const Privilege& privilege = *it;
-
- ParsedPrivilege parsedPrivilege;
- std::string errmsg;
- if (!ParsedPrivilege::privilegeToParsedPrivilege(privilege, &parsedPrivilege, &errmsg)) {
- return Status(ErrorCodes::FailedToParse, errmsg);
- }
- if (!parsedPrivilege.isValid(&errmsg)) {
- return Status(ErrorCodes::FailedToParse, errmsg);
- }
- arrBuilder.append(parsedPrivilege.toBSON());
- }
- *result = arrBuilder.arr();
+ // privileges may come in with non-unique ResourcePatterns.
+ // Make a local copy so that ActionSets are merged.
+ PrivilegeVector uniquePrivileges;
+ Privilege::addPrivilegesToPrivilegeVector(&uniquePrivileges, privileges);
+ *result = containerToBSONArray(uniquePrivileges);
return Status::OK();
}
@@ -158,47 +134,42 @@ Status getCurrentUserRoles(OperationContext* opCtx,
*/
Status checkOkayToGrantRolesToRole(OperationContext* opCtx,
const RoleName& role,
- const std::vector<RoleName> rolesToAdd,
+ const std::vector<RoleName>& rolesToAdd,
AuthorizationManager* authzManager) {
- for (std::vector<RoleName>::const_iterator it = rolesToAdd.begin(); it != rolesToAdd.end();
- ++it) {
- const RoleName& roleToAdd = *it;
+ for (const auto& roleToAdd : rolesToAdd) {
if (roleToAdd == role) {
- return Status(ErrorCodes::InvalidRoleModification,
- str::stream()
- << "Cannot grant role " << role.getFullName() << " to itself.");
+ return {ErrorCodes::InvalidRoleModification,
+ str::stream() << "Cannot grant role " << role.getFullName() << " to itself."};
}
if (role.getDB() != "admin" && roleToAdd.getDB() != role.getDB()) {
- return Status(ErrorCodes::InvalidRoleModification,
- str::stream()
- << "Roles on the \'" << role.getDB()
- << "\' database cannot be granted roles from other databases");
+ return {ErrorCodes::InvalidRoleModification,
+ str::stream() << "Roles on the \'" << role.getDB()
+ << "\' database cannot be granted roles from other databases"};
}
+ }
- BSONObj roleToAddDoc;
- Status status = authzManager->getRoleDescription(opCtx, roleToAdd, &roleToAddDoc);
- if (status == ErrorCodes::RoleNotFound) {
- return Status(ErrorCodes::RoleNotFound,
- "Cannot grant nonexistent role " + roleToAdd.toString());
- }
- if (!status.isOK()) {
- return status;
- }
- std::vector<RoleName> indirectRoles;
- status = auth::parseRoleNamesFromBSONArray(
- BSONArray(roleToAddDoc["inheritedRoles"].Obj()), role.getDB(), &indirectRoles);
- if (!status.isOK()) {
- return status;
- }
+ auto status = authzManager->rolesExist(opCtx, rolesToAdd);
+ if (!status.isOK()) {
+ return {status.code(),
+ str::stream() << "Cannot grant roles to '" << role.toString()
+ << "': " << status.reason()};
+ }
- if (sequenceContains(indirectRoles, role)) {
- return Status(ErrorCodes::InvalidRoleModification,
- str::stream() << "Granting " << roleToAdd.getFullName() << " to "
- << role.getFullName()
- << " would introduce a cycle in the role graph.");
- }
+ auto swData = authzManager->resolveRoles(
+ opCtx, rolesToAdd, AuthorizationManager::ResolveRoleOption::kRoles);
+ if (!swData.isOK()) {
+ return {swData.getStatus().code(),
+ str::stream() << "Cannot grant roles to '" << role.toString()
+ << "': " << swData.getStatus().reason()};
+ }
+
+ if (sequenceContains(swData.getValue().roles.get(), role)) {
+ return {ErrorCodes::InvalidRoleModification,
+ str::stream() << "Granting roles to " << role.getFullName()
+ << " would introduce a cycle in the role graph"};
}
+
return Status::OK();
}
@@ -1093,7 +1064,7 @@ void CmdUMCTyped<GrantRolesToUserCommand, void>::Invocation::typedRun(OperationC
}
audit::logGrantRolesToUser(client, userName, resolvedRoleNames);
- auto newRolesBSONArray = roleSetToBSONArray(userRoles);
+ auto newRolesBSONArray = containerToBSONArray(userRoles);
auto status = updatePrivilegeDocument(
opCtx, userName, BSON("$set" << BSON("roles" << newRolesBSONArray)));
@@ -1128,7 +1099,7 @@ void CmdUMCTyped<RevokeRolesFromUserCommand, void>::Invocation::typedRun(Operati
}
audit::logRevokeRolesFromUser(client, userName, resolvedUserRoles);
- BSONArray newRolesBSONArray = roleSetToBSONArray(userRoles);
+ BSONArray newRolesBSONArray = containerToBSONArray(userRoles);
auto status = updatePrivilegeDocument(
opCtx, userName, BSON("$set" << BSON("roles" << newRolesBSONArray)));
@@ -1323,7 +1294,7 @@ void CmdUMCTyped<CreateRoleCommand, void>::Invocation::typedRun(OperationContext
roleObjBuilder.append("privileges", privileges);
auto resolvedRoleNames = auth::resolveRoleNames(cmd.getRoles(), dbname);
- roleObjBuilder.append("roles", rolesVectorToBSONArray(resolvedRoleNames));
+ roleObjBuilder.append("roles", containerToBSONArray(resolvedRoleNames));
boost::optional<BSONArray> bsonAuthRestrictions;
if (auto ar = cmd.getAuthenticationRestrictions(); ar && !ar->empty()) {
@@ -1372,7 +1343,7 @@ void CmdUMCTyped<UpdateRoleCommand, void>::Invocation::typedRun(OperationContext
boost::optional<std::vector<RoleName>> optRoles;
if (auto roles = cmd.getRoles()) {
optRoles = auth::resolveRoleNames(roles.get(), dbname);
- updateSetBuilder.append("roles", rolesVectorToBSONArray(*optRoles));
+ updateSetBuilder.append("roles", containerToBSONArray(*optRoles));
}
BSONArray authRest;
@@ -1444,17 +1415,10 @@ void CmdUMCTyped<GrantPrivilegesToRoleCommand, void>::Invocation::typedRun(
uassertStatusOK(checkOkayToGrantPrivilegesToRole(roleName, cmd.getPrivileges()));
- BSONObj roleDoc;
- uassertStatusOK(authzManager->getRoleDescription(opCtx,
- roleName,
- PrivilegeFormat::kShowSeparate,
- AuthenticationRestrictionsFormat::kOmit,
- &roleDoc));
-
- PrivilegeVector privileges;
- uassertStatusOK(
- auth::parseAndValidatePrivilegeArray(BSONArray(roleDoc["privileges"].Obj()), &privileges));
-
+ // Add additional privileges to existing set.
+ auto data = uassertStatusOK(authzManager->resolveRoles(
+ opCtx, {roleName}, AuthorizationManager::ResolveRoleOption::kDirectPrivileges));
+ auto privileges = std::move(data.privileges.get());
for (const auto& priv : cmd.getPrivileges()) {
Privilege::addPrivilegeToPrivilegeVector(&privileges, priv);
}
@@ -1499,17 +1463,9 @@ void CmdUMCTyped<RevokePrivilegesFromRoleCommand, void>::Invocation::typedRun(
auto* authzManager = AuthorizationManager::get(serviceContext);
auto lk = uassertStatusOK(requireWritableAuthSchema28SCRAM(opCtx, authzManager));
- BSONObj roleDoc;
- uassertStatusOK(authzManager->getRoleDescription(opCtx,
- roleName,
- PrivilegeFormat::kShowSeparate,
- AuthenticationRestrictionsFormat::kOmit,
- &roleDoc));
-
- PrivilegeVector privileges;
- uassertStatusOK(
- auth::parseAndValidatePrivilegeArray(BSONArray(roleDoc["privileges"].Obj()), &privileges));
-
+ auto data = uassertStatusOK(authzManager->resolveRoles(
+ opCtx, {roleName}, AuthorizationManager::ResolveRoleOption::kDirectPrivileges));
+ auto privileges = std::move(data.privileges.get());
for (const auto& rmPriv : cmd.getPrivileges()) {
for (auto it = privileges.begin(); it != privileges.end(); ++it) {
if (it->getResourcePattern() == rmPriv.getResourcePattern()) {
@@ -1563,28 +1519,19 @@ void CmdUMCTyped<GrantRolesToRoleCommand, void>::Invocation::typedRun(OperationC
auto* authzManager = AuthorizationManager::get(serviceContext);
auto lk = uassertStatusOK(requireWritableAuthSchema28SCRAM(opCtx, authzManager));
- // Role existence has to be checked after acquiring the update lock
- BSONObj roleDoc;
- uassertStatusOK(authzManager->getRoleDescription(opCtx, roleName, &roleDoc));
-
// Check for cycles
uassertStatusOK(checkOkayToGrantRolesToRole(opCtx, roleName, rolesToAdd, authzManager));
// Add new roles to existing roles
- std::vector<RoleName> directRoles;
- uassertStatusOK(auth::parseRoleNamesFromBSONArray(
- BSONArray(roleDoc["roles"].Obj()), roleName.getDB(), &directRoles));
- for (const auto& roleToAdd : rolesToAdd) {
- if (!sequenceContains(directRoles, roleToAdd)) {
- // Don't double-add role
- directRoles.push_back(roleToAdd);
- }
- }
+ auto data = uassertStatusOK(authzManager->resolveRoles(
+ opCtx, {roleName}, AuthorizationManager::ResolveRoleOption::kDirectRoles));
+ auto directRoles = std::move(data.roles.get());
+ directRoles.insert(rolesToAdd.cbegin(), rolesToAdd.cend());
audit::logGrantRolesToRole(client, roleName, rolesToAdd);
auto status = updateRoleDocument(
- opCtx, roleName, BSON("$set" << BSON("roles" << rolesVectorToBSONArray(directRoles))));
+ opCtx, roleName, BSON("$set" << BSON("roles" << containerToBSONArray(directRoles))));
// Must invalidate even on bad status - what if the write succeeded but the GLE failed?
authzManager->invalidateUserCache(opCtx);
uassertStatusOK(status);
@@ -1612,23 +1559,18 @@ void CmdUMCTyped<RevokeRolesFromRoleCommand, void>::Invocation::typedRun(Operati
auto* authzManager = AuthorizationManager::get(serviceContext);
auto lk = uassertStatusOK(requireWritableAuthSchema28SCRAM(opCtx, authzManager));
- BSONObj roleDoc;
- uassertStatusOK(authzManager->getRoleDescription(opCtx, roleName, &roleDoc));
-
- std::vector<RoleName> roles;
- uassertStatusOK(auth::parseRoleNamesFromBSONArray(
- BSONArray(roleDoc["roles"].Obj()), roleName.getDB(), &roles));
-
+ // Remove roles from existing set.
+ auto data = uassertStatusOK(authzManager->resolveRoles(
+ opCtx, {roleName}, AuthorizationManager::ResolveRoleOption::kDirectRoles));
+ auto roles = std::move(data.roles.get());
for (const auto& roleToRemove : rolesToRemove) {
- if (auto it = std::find(roles.begin(), roles.end(), roleToRemove); it != roles.end()) {
- roles.erase(it);
- }
+ roles.erase(roleToRemove);
}
audit::logRevokeRolesFromRole(client, roleName, rolesToRemove);
auto status = updateRoleDocument(
- opCtx, roleName, BSON("$set" << BSON("roles" << rolesVectorToBSONArray(roles))));
+ opCtx, roleName, BSON("$set" << BSON("roles" << containerToBSONArray(roles))));
// Must invalidate even on bad status - what if the write succeeded but the GLE failed?
authzManager->invalidateUserCache(opCtx);
uassertStatusOK(status);
@@ -1850,39 +1792,32 @@ public:
const BSONObj& cmdObj,
BSONObjBuilder& result) override {
auth::RolesInfoArgs args;
- Status status = auth::parseRolesInfoCommand(cmdObj, dbname, &args);
- uassertStatusOK(status);
+ uassertStatusOK(auth::parseRolesInfoCommand(cmdObj, dbname, &args));
AuthorizationManager* authzManager = AuthorizationManager::get(opCtx->getServiceContext());
auto lk = uassertStatusOK(requireReadableAuthSchema26Upgrade(opCtx, authzManager));
if (args.allForDB) {
- std::vector<BSONObj> rolesDocs;
- status = authzManager->getRoleDescriptionsForDB(opCtx,
- dbname,
- args.privilegeFormat,
- args.authenticationRestrictionsFormat,
- args.showBuiltinRoles,
- &rolesDocs);
- uassertStatusOK(status);
-
if (args.privilegeFormat == PrivilegeFormat::kShowAsUserFragment) {
uasserted(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());
- } else {
- BSONObj roleDetails;
- status = authzManager->getRolesDescription(opCtx,
- args.roleNames,
+
+ BSONArrayBuilder rolesBuilder(result.subarrayStart("roles"));
+ uassertStatusOK(
+ authzManager->getRoleDescriptionsForDB(opCtx,
+ dbname,
args.privilegeFormat,
args.authenticationRestrictionsFormat,
- &roleDetails);
- uassertStatusOK(status);
+ args.showBuiltinRoles,
+ &rolesBuilder));
+ } else {
+ BSONObj roleDetails;
+ uassertStatusOK(authzManager->getRolesDescription(opCtx,
+ args.roleNames,
+ args.privilegeFormat,
+ args.authenticationRestrictionsFormat,
+ &roleDetails));
if (args.privilegeFormat == PrivilegeFormat::kShowAsUserFragment) {
result.append("userFragment", roleDetails);
diff --git a/src/mongo/db/commands/user_management_commands_common.cpp b/src/mongo/db/commands/user_management_commands_common.cpp
index 83df92fae4c..d10742585a1 100644
--- a/src/mongo/db/commands/user_management_commands_common.cpp
+++ b/src/mongo/db/commands/user_management_commands_common.cpp
@@ -53,12 +53,12 @@ namespace auth {
std::vector<RoleName> resolveRoleNames(const std::vector<RoleNameOrString>& possibleRoles,
StringData dbname) {
- std::vector<RoleName> roles;
- std::transform(possibleRoles.cbegin(),
- possibleRoles.cend(),
- std::back_inserter(roles),
- [dbname](const auto& possibleRole) { return possibleRole.getRoleName(dbname); });
- return roles;
+ // De-duplicate as we resolve names by using a set.
+ stdx::unordered_set<RoleName> roles;
+ for (const auto& possibleRole : possibleRoles) {
+ roles.insert(possibleRole.getRoleName(dbname));
+ }
+ return std::vector<RoleName>(roles.cbegin(), roles.cend());
}
Status checkAuthorizedToGrantRoles(AuthorizationSession* authzSession,