summaryrefslogtreecommitdiff
path: root/src/mongo/db/commands
diff options
context:
space:
mode:
authorSara Golemon <sara.golemon@mongodb.com>2020-08-08 19:53:46 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-08-24 23:19:05 +0000
commitfc279d5cbaa398d879513ae2d679408bfda69e40 (patch)
tree860ffd30730befdf4071284bfb8d6046682e6f3b /src/mongo/db/commands
parent5112cd025d54dab920b16bf6ebc901e2d7b4c13e (diff)
downloadmongo-fc279d5cbaa398d879513ae2d679408bfda69e40.tar.gz
SERVER-50204 Refactor role acquisition using resolveRoles
Diffstat (limited to 'src/mongo/db/commands')
-rw-r--r--src/mongo/db/commands/user_management_commands.cpp215
-rw-r--r--src/mongo/db/commands/user_management_commands_common.cpp12
2 files changed, 81 insertions, 146 deletions
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,