diff options
author | Sara Golemon <sara.golemon@mongodb.com> | 2020-08-08 19:53:46 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2020-08-24 23:19:05 +0000 |
commit | fc279d5cbaa398d879513ae2d679408bfda69e40 (patch) | |
tree | 860ffd30730befdf4071284bfb8d6046682e6f3b /src/mongo/db/commands | |
parent | 5112cd025d54dab920b16bf6ebc901e2d7b4c13e (diff) | |
download | mongo-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.cpp | 215 | ||||
-rw-r--r-- | src/mongo/db/commands/user_management_commands_common.cpp | 12 |
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, |