diff options
author | Sara Golemon <sara.golemon@mongodb.com> | 2020-10-26 17:26:31 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2020-11-09 18:16:52 +0000 |
commit | 5e0d73d0d8e559e34203740af93b6ca03d573ea5 (patch) | |
tree | 3ddc4e4ddde8af5fb575ffe819d0bb4b3acee271 /src/mongo/db/commands | |
parent | fa826f6a5b77eb059fe03d411276c3ee7eb303d5 (diff) | |
download | mongo-5e0d73d0d8e559e34203740af93b6ca03d573ea5.tar.gz |
SERVER-51864 IDLify usersInfo and rolesInfo commands
Diffstat (limited to 'src/mongo/db/commands')
-rw-r--r-- | src/mongo/db/commands/user_management_commands.cpp | 360 | ||||
-rw-r--r-- | src/mongo/db/commands/user_management_commands.idl | 88 | ||||
-rw-r--r-- | src/mongo/db/commands/user_management_commands_common.cpp | 104 | ||||
-rw-r--r-- | src/mongo/db/commands/user_management_commands_common.h | 10 |
4 files changed, 296 insertions, 266 deletions
diff --git a/src/mongo/db/commands/user_management_commands.cpp b/src/mongo/db/commands/user_management_commands.cpp index bdc40e745cb..44edd0d4fd8 100644 --- a/src/mongo/db/commands/user_management_commands.cpp +++ b/src/mongo/db/commands/user_management_commands.cpp @@ -906,12 +906,24 @@ private: TransactionState _state = TransactionState::kInit; }; -template <typename RequestT, typename ReplyT> -class CmdUMCTyped : public TypedCommand<CmdUMCTyped<RequestT, ReplyT>> { +// Used by most UMC commands. +struct UMCStdParams { + static constexpr bool supportsWriteConcern = true; + static constexpr auto allowedOnSecondary = BasicCommand::AllowedOnSecondary::kNever; +}; + +// Used by {usersInfo:...} and {rolesInfo:...} +struct UMCInfoParams { + static constexpr bool supportsWriteConcern = false; + static constexpr auto allowedOnSecondary = BasicCommand::AllowedOnSecondary::kOptIn; +}; + +template <typename RequestT, typename ReplyT, typename Params = UMCStdParams> +class CmdUMCTyped : public TypedCommand<CmdUMCTyped<RequestT, ReplyT, Params>> { public: using Request = RequestT; using Reply = ReplyT; - using TC = TypedCommand<CmdUMCTyped<RequestT, ReplyT>>; + using TC = TypedCommand<CmdUMCTyped<RequestT, ReplyT, Params>>; class Invocation final : public TC::InvocationBase { public: @@ -922,7 +934,7 @@ public: private: bool supportsWriteConcern() const final { - return true; + return Params::supportsWriteConcern; } void doCheckAuthorization(OperationContext* opCtx) const final { @@ -935,7 +947,7 @@ public: }; typename TC::AllowedOnSecondary secondaryAllowed(ServiceContext*) const final { - return TC::AllowedOnSecondary::kNever; + return Params::allowedOnSecondary; } }; @@ -1282,160 +1294,134 @@ void CmdUMCTyped<RevokeRolesFromUserCommand, void>::Invocation::typedRun(Operati uassertStatusOK(status); } -class CmdUsersInfo : public BasicCommand { -public: - CmdUsersInfo() : BasicCommand("usersInfo") {} - - AllowedOnSecondary secondaryAllowed(ServiceContext*) const override { - return AllowedOnSecondary::kOptIn; - } - - bool supportsWriteConcern(const BSONObj& cmd) const override { - return false; - } +CmdUMCTyped<UsersInfoCommand, UsersInfoReply, UMCInfoParams> cmdUsersInfo; +template <> +UsersInfoReply CmdUMCTyped<UsersInfoCommand, UsersInfoReply, UMCInfoParams>::Invocation::typedRun( + OperationContext* opCtx) { + const auto& cmd = request(); + const auto& arg = cmd.getCommandParameter(); + const auto& dbname = cmd.getDbName(); - std::string help() const override { - return "Returns information about users."; - } + auto* authzManager = AuthorizationManager::get(opCtx->getServiceContext()); + auto lk = uassertStatusOK(requireReadableAuthSchema26Upgrade(opCtx, authzManager)); - Status checkAuthForCommand(Client* client, - const std::string& dbname, - const BSONObj& cmdObj) const override { - return auth::checkAuthForUsersInfoCommand(client, dbname, cmdObj); - } + std::vector<BSONObj> users; + if (cmd.getShowPrivileges() || cmd.getShowAuthenticationRestrictions()) { + uassert(ErrorCodes::IllegalOperation, + "Privilege or restriction details require exact-match usersInfo queries", + !cmd.getFilter() && arg.isExact()); + + // If you want privileges or restrictions you need to call getUserDescription + // on each user. + for (const auto& userName : arg.getElements(dbname)) { + BSONObj userDetails; + auto status = authzManager->getUserDescription(opCtx, userName, &userDetails); + if (status.code() == ErrorCodes::UserNotFound) { + continue; + } + uassertStatusOK(status); - bool run(OperationContext* opCtx, - const std::string& dbname, - const BSONObj& cmdObj, - BSONObjBuilder& result) override { - auth::UsersInfoArgs args; - uassertStatusOK(auth::parseUsersInfoCommand(cmdObj, dbname, &args)); - - auto* authzManager = AuthorizationManager::get(opCtx->getServiceContext()); - auto lk = uassertStatusOK(requireReadableAuthSchema26Upgrade(opCtx, authzManager)); - - BSONArrayBuilder usersArrayBuilder(result.subarrayStart("users")); - if (args.showPrivileges || - (args.authenticationRestrictionsFormat == AuthenticationRestrictionsFormat::kShow)) { - uassert(ErrorCodes::IllegalOperation, - "Privilege or restriction details require exact-match usersInfo queries", - !args.filter && (args.target == auth::UsersInfoArgs::Target::kExplicitUsers)); - - // If you want privileges or restrictions you need to call getUserDescription - // on each user. - for (const auto& userName : args.userNames) { - BSONObj userDetails; - auto status = authzManager->getUserDescription(opCtx, userName, &userDetails); - if (status.code() == ErrorCodes::UserNotFound) { - continue; - } - uassertStatusOK(status); - - // getUserDescription always includes credentials and restrictions, which may need - // to be stripped out - BSONObjBuilder strippedUser(usersArrayBuilder.subobjStart()); - for (const BSONElement& e : userDetails) { - if (e.fieldNameStringData() == "credentials") { - BSONArrayBuilder mechanismNamesBuilder; - BSONObj mechanismsObj = e.Obj(); - for (const BSONElement& mechanismElement : mechanismsObj) { - mechanismNamesBuilder.append(mechanismElement.fieldNameStringData()); - } - strippedUser.append("mechanisms", mechanismNamesBuilder.arr()); - - if (!args.showCredentials) { - continue; - } + // getUserDescription always includes credentials and restrictions, which may need + // to be stripped out + BSONObjBuilder strippedUser; + for (const BSONElement& e : userDetails) { + if (e.fieldNameStringData() == "credentials") { + BSONArrayBuilder mechanismNamesBuilder; + BSONObj mechanismsObj = e.Obj(); + for (const BSONElement& mechanismElement : mechanismsObj) { + mechanismNamesBuilder.append(mechanismElement.fieldNameStringData()); } + strippedUser.append("mechanisms", mechanismNamesBuilder.arr()); - if (e.fieldNameStringData() == "authenticationRestrictions" && - args.authenticationRestrictionsFormat == - AuthenticationRestrictionsFormat::kOmit) { + if (!cmd.getShowCredentials()) { continue; } - - strippedUser.append(e); } - strippedUser.doneFast(); - } - } else { - // If you don't need privileges, or authenticationRestrictions, you can just do a - // regular query on system.users - std::vector<BSONObj> pipeline; - - if (args.target == auth::UsersInfoArgs::Target::kGlobal) { - // Leave the pipeline unconstrained, we want to return every user. - } else if (args.target == auth::UsersInfoArgs::Target::kDB) { - pipeline.push_back( - BSON("$match" << BSON(AuthorizationManager::USER_DB_FIELD_NAME << dbname))); - } else { - BSONArrayBuilder usersMatchArray; - for (size_t i = 0; i < args.userNames.size(); ++i) { - usersMatchArray.append(BSON(AuthorizationManager::USER_NAME_FIELD_NAME - << args.userNames[i].getUser() - << AuthorizationManager::USER_DB_FIELD_NAME - << args.userNames[i].getDB())); + + if ((e.fieldNameStringData() == "authenticationRestrictions") && + !cmd.getShowAuthenticationRestrictions()) { + continue; } - pipeline.push_back(BSON("$match" << BSON("$or" << usersMatchArray.arr()))); - } - // Order results by user field then db field, matching how UserNames are ordered - pipeline.push_back(BSON("$sort" << BSON("user" << 1 << "db" << 1))); + strippedUser.append(e); + } + users.push_back(strippedUser.obj()); + } + } else { + // If you don't need privileges, or authenticationRestrictions, you can just do a + // regular query on system.users + std::vector<BSONObj> pipeline; - // Rewrite the credentials object into an array of its fieldnames. + if (arg.isAllForAllDBs()) { + // Leave the pipeline unconstrained, we want to return every user. + } else if (arg.isAllOnCurrentDB()) { pipeline.push_back( - BSON("$addFields" << BSON("mechanisms" - << BSON("$map" << BSON("input" << BSON("$objectToArray" - << "$credentials") - << "as" - << "cred" - << "in" - << "$$cred.k"))))); - - if (args.showCredentials) { - // Authentication restrictions are only rendered in the single user case. - pipeline.push_back(BSON("$unset" - << "authenticationRestrictions")); - } else { - // Remove credentials as well, they're not required in the output - pipeline.push_back(BSON("$unset" << BSON_ARRAY("authenticationRestrictions" - << "credentials"))); + BSON("$match" << BSON(AuthorizationManager::USER_DB_FIELD_NAME << dbname))); + } else { + invariant(arg.isExact()); + BSONArrayBuilder usersMatchArray; + for (const auto& userName : arg.getElements(dbname)) { + usersMatchArray.append(userName.toBSON()); } + pipeline.push_back(BSON("$match" << BSON("$or" << usersMatchArray.arr()))); + } - // Handle a user specified filter. - if (args.filter) { - pipeline.push_back(BSON("$match" << *args.filter)); - } + // Order results by user field then db field, matching how UserNames are ordered + pipeline.push_back(BSON("$sort" << BSON("user" << 1 << "db" << 1))); + + // Rewrite the credentials object into an array of its fieldnames. + pipeline.push_back( + BSON("$addFields" << BSON("mechanisms" + << BSON("$map" << BSON("input" << BSON("$objectToArray" + << "$credentials") + << "as" + << "cred" + << "in" + << "$$cred.k"))))); + + if (cmd.getShowCredentials()) { + // Authentication restrictions are only rendered in the single user case. + pipeline.push_back(BSON("$unset" + << "authenticationRestrictions")); + } else { + // Remove credentials as well, they're not required in the output + pipeline.push_back(BSON("$unset" << BSON_ARRAY("authenticationRestrictions" + << "credentials"))); + } - DBDirectClient client(opCtx); - - rpc::OpMsgReplyBuilder replyBuilder; - AggregationRequest aggRequest(AuthorizationManager::usersCollectionNamespace, - std::move(pipeline)); - // Impose no cursor privilege requirements, as cursor is drained internally - uassertStatusOK(runAggregate(opCtx, - AuthorizationManager::usersCollectionNamespace, - aggRequest, - aggRequest.serializeToCommandObj().toBson(), - PrivilegeVector(), - &replyBuilder)); - auto bodyBuilder = replyBuilder.getBodyBuilder(); - CommandHelpers::appendSimpleCommandStatus(bodyBuilder, true); - bodyBuilder.doneFast(); - auto response = CursorResponse::parseFromBSONThrowing(replyBuilder.releaseBody()); - DBClientCursor cursor( - &client, response.getNSS(), response.getCursorId(), 0, 0, response.releaseBatch()); - - while (cursor.more()) { - usersArrayBuilder.append(cursor.next()); - } + // Handle a user specified filter. + if (auto filter = cmd.getFilter()) { + pipeline.push_back(BSON("$match" << *filter)); } - usersArrayBuilder.doneFast(); - return true; + DBDirectClient client(opCtx); + + rpc::OpMsgReplyBuilder replyBuilder; + AggregationRequest aggRequest(AuthorizationManager::usersCollectionNamespace, + std::move(pipeline)); + // Impose no cursor privilege requirements, as cursor is drained internally + uassertStatusOK(runAggregate(opCtx, + AuthorizationManager::usersCollectionNamespace, + aggRequest, + aggRequest.serializeToCommandObj().toBson(), + PrivilegeVector(), + &replyBuilder)); + auto bodyBuilder = replyBuilder.getBodyBuilder(); + CommandHelpers::appendSimpleCommandStatus(bodyBuilder, true); + bodyBuilder.doneFast(); + auto response = CursorResponse::parseFromBSONThrowing(replyBuilder.releaseBody()); + DBClientCursor cursor( + &client, response.getNSS(), response.getCursorId(), 0, 0, response.releaseBatch()); + + while (cursor.more()) { + users.push_back(cursor.next().getOwned()); + } } -} cmdUsersInfo; + UsersInfoReply reply; + reply.setUsers(std::move(users)); + return reply; +} CmdUMCTyped<CreateRoleCommand, void> cmdCreateRole; template <> @@ -1907,71 +1893,55 @@ CmdUMCTyped<DropAllRolesFromDatabaseCommand, DropAllRolesFromDatabaseReply>::Inv * these roles. This format may change over time with changes to the auth * schema. */ -class CmdRolesInfo : public BasicCommand { -public: - CmdRolesInfo() : BasicCommand("rolesInfo") {} - - AllowedOnSecondary secondaryAllowed(ServiceContext*) const override { - return AllowedOnSecondary::kOptIn; - } - - bool supportsWriteConcern(const BSONObj& cmd) const override { - return false; - } +CmdUMCTyped<RolesInfoCommand, RolesInfoReply, UMCInfoParams> cmdRolesInfo; +template <> +RolesInfoReply CmdUMCTyped<RolesInfoCommand, RolesInfoReply, UMCInfoParams>::Invocation::typedRun( + OperationContext* opCtx) { + const auto& cmd = request(); + const auto& arg = cmd.getCommandParameter(); + const auto& dbname = cmd.getDbName(); - std::string help() const override { - return "Returns information about roles."; - } + auto* authzManager = AuthorizationManager::get(opCtx->getServiceContext()); + auto lk = uassertStatusOK(requireReadableAuthSchema26Upgrade(opCtx, authzManager)); - Status checkAuthForCommand(Client* client, - const std::string& dbname, - const BSONObj& cmdObj) const override { - return auth::checkAuthForRolesInfoCommand(client, dbname, cmdObj); - } + // Only usersInfo actually supports {forAllDBs: 1} mode. + invariant(!arg.isAllForAllDBs()); - bool run(OperationContext* opCtx, - const std::string& dbname, - const BSONObj& cmdObj, - BSONObjBuilder& result) override { - auth::RolesInfoArgs args; - uassertStatusOK(auth::parseRolesInfoCommand(cmdObj, dbname, &args)); + auto privFmt = *(cmd.getShowPrivileges()); + auto restrictionFormat = cmd.getShowAuthenticationRestrictions() + ? AuthenticationRestrictionsFormat::kShow + : AuthenticationRestrictionsFormat::kOmit; - AuthorizationManager* authzManager = AuthorizationManager::get(opCtx->getServiceContext()); - auto lk = uassertStatusOK(requireReadableAuthSchema26Upgrade(opCtx, authzManager)); + RolesInfoReply reply; + if (arg.isAllOnCurrentDB()) { - if (args.allForDB) { - if (args.privilegeFormat == PrivilegeFormat::kShowAsUserFragment) { - uasserted(ErrorCodes::IllegalOperation, - "Cannot get user fragment for all roles in a database"); - } + uassert(ErrorCodes::IllegalOperation, + "Cannot get user fragment for all roles in a database", + privFmt != PrivilegeFormat::kShowAsUserFragment); - BSONArrayBuilder rolesBuilder(result.subarrayStart("roles")); - uassertStatusOK( - authzManager->getRoleDescriptionsForDB(opCtx, - dbname, - args.privilegeFormat, - args.authenticationRestrictionsFormat, - args.showBuiltinRoles, - &rolesBuilder)); + std::vector<BSONObj> roles; + uassertStatusOK(authzManager->getRoleDescriptionsForDB( + opCtx, dbname, privFmt, restrictionFormat, cmd.getShowBuiltinRoles(), &roles)); + reply.setRoles(std::move(roles)); + } else { + invariant(arg.isExact()); + auto roleNames = arg.getElements(dbname); + + if (privFmt == PrivilegeFormat::kShowAsUserFragment) { + BSONObj fragment; + uassertStatusOK(authzManager->getRolesAsUserFragment( + opCtx, roleNames, restrictionFormat, &fragment)); + reply.setUserFragment(fragment); } else { - BSONObj roleDetails; - uassertStatusOK(authzManager->getRolesDescription(opCtx, - args.roleNames, - args.privilegeFormat, - args.authenticationRestrictionsFormat, - &roleDetails)); - - if (args.privilegeFormat == PrivilegeFormat::kShowAsUserFragment) { - result.append("userFragment", roleDetails); - } else { - result.append("roles", BSONArray(roleDetails)); - } + std::vector<BSONObj> roles; + uassertStatusOK(authzManager->getRolesDescription( + opCtx, roleNames, privFmt, restrictionFormat, &roles)); + reply.setRoles(std::move(roles)); } - - return true; } -} cmdRolesInfo; + return reply; +} class CmdInvalidateUserCache : public BasicCommand { public: diff --git a/src/mongo/db/commands/user_management_commands.idl b/src/mongo/db/commands/user_management_commands.idl index 406b5924164..0dfbdd9fb17 100644 --- a/src/mongo/db/commands/user_management_commands.idl +++ b/src/mongo/db/commands/user_management_commands.idl @@ -32,6 +32,7 @@ imports: - "mongo/idl/basic_types.idl" - "mongo/db/auth/auth_types.idl" - "mongo/db/auth/address_restriction.idl" + - "mongo/db/auth/user_management_commands_parser.idl" server_parameters: enforceUserClusterSeparation: @@ -60,6 +61,27 @@ structs: type: int cpp_name: count + usersInfoReply: + description: "Reply from usersInfo command" + strict: false + fields: + users: + description: "Users descriptions" + type: array<object_owned> + + rolesInfoReply: + description: "Reply from usersInfo command" + strict: false + fields: + roles: + description: "Users descriptions" + type: array<object_owned> + optional: true + userFragment: + description: "Roles as user document fragment" + type: object_owned + optional: true + UMCTransactionFailPoint: description: Data for umcTransaction failpoint fields: @@ -90,7 +112,7 @@ commands: type: array<RoleNameOrString> digestPassword: description: "True if the server should digest the password, false for pre-digested" - type: bool + type: safeBool default: true writeConcern: description: "The level of write concern for the creation operation" @@ -127,7 +149,7 @@ commands: optional: true digestPassword: description: "True if the server should digest the password, false for pre-digested" - type: bool + type: safeBool default: true writeConcern: description: "The level of write concern for the update operation" @@ -291,3 +313,65 @@ commands: namespace: ignored cpp_name: DropAllRolesFromDatabaseCommand strict: true + + usersInfo: + description: "Returns information about users." + command_name: usersInfo + namespace: type + type: UsersInfoCommandArg + cpp_name: UsersInfoCommand + strict: true + fields: + showPrivileges: + description: >- + Set the field to true to show the user’s full set of privileges, + including expanded information for the inherited roles. + If viewing all users, you cannot specify this field. + type: safeBool + default: false + showCredentials: + description: >- + Set the field to true to display the user’s password hash. + type: safeBool + default: false + showAuthenticationRestrictions: + description: >- + Set the field to true to show the user’s authentication restrictions. + If viewing all users, you cannot specify this field. + type: safeBool + default: false + filter: + description: >- + A document that specifies $match stage conditions to return information + for users that match the filter conditions. + type: object + optional: true + + rolesInfo: + description: "returns information about roles." + command_name: rolesInfo + namespace: type + type: RolesInfoCommandArg + cpp_name: RolesInfoCommand + strict: true + fields: + showPrivileges: + description: >- + Set the field to true to show the user’s full set of privileges, + including expanded information for the inherited roles. + If viewing all roles, you cannot specify this field. + type: ParsedPrivilegeFormat + default: false + showBuiltinRoles: + description: >- + When the rolesInfo field is set to 1, set showBuiltinRoles to true + to include built-in roles in the output. + Otherwise the output for rolesInfo: 1 displays only user-defined roles. + type: safeBool + default: false + showAuthenticationRestrictions: + description: >- + Set the field to true to show the user’s authentication restrictions. + If viewing all users, you cannot specify this field. + type: safeBool + default: false diff --git a/src/mongo/db/commands/user_management_commands_common.cpp b/src/mongo/db/commands/user_management_commands_common.cpp index d10742585a1..06c4f2683ea 100644 --- a/src/mongo/db/commands/user_management_commands_common.cpp +++ b/src/mongo/db/commands/user_management_commands_common.cpp @@ -273,45 +273,35 @@ void checkAuthForTypedCommand(Client* client, const RevokeRolesFromRoleCommand& uassertStatusOK(checkAuthorizedToRevokeRoles(as, rolesToRemove)); } -Status checkAuthForUsersInfoCommand(Client* client, - const std::string& dbname, - const BSONObj& cmdObj) { - AuthorizationSession* authzSession = AuthorizationSession::get(client); - auth::UsersInfoArgs args; - Status status = auth::parseUsersInfoCommand(cmdObj, dbname, &args); - if (!status.isOK()) { - return status; - } +void checkAuthForTypedCommand(Client* client, const UsersInfoCommand& request) { + const auto& dbname = request.getDbName(); + const auto& arg = request.getCommandParameter(); + auto* as = AuthorizationSession::get(client); - if (args.target == auth::UsersInfoArgs::Target::kDB) { - if (!authzSession->isAuthorizedForActionsOnResource( - ResourcePattern::forDatabaseName(dbname), ActionType::viewUser)) { - return Status(ErrorCodes::Unauthorized, - str::stream() - << "Not authorized to view users from the " << dbname << " database"); - } - } else if (args.target == auth::UsersInfoArgs::Target::kGlobal) { - if (!authzSession->isAuthorizedForActionsOnResource(ResourcePattern::forClusterResource(), - ActionType::viewUser)) { - return Status(ErrorCodes::Unauthorized, - str::stream() << "Not authorized to view users from all" - << " databases"); - } + if (arg.isAllOnCurrentDB()) { + uassert(ErrorCodes::Unauthorized, + str::stream() << "Not authorized to view users from the " << dbname << " database", + as->isAuthorizedForActionsOnResource(ResourcePattern::forDatabaseName(dbname), + ActionType::viewUser)); + } else if (arg.isAllForAllDBs()) { + uassert(ErrorCodes::Unauthorized, + str::stream() << "Not authorized to view users from all databases", + as->isAuthorizedForActionsOnResource(ResourcePattern::forClusterResource(), + ActionType::viewUser)); } else { - for (size_t i = 0; i < args.userNames.size(); ++i) { - if (authzSession->lookupUser(args.userNames[i])) { - continue; // Can always view users you are logged in as - } - if (!authzSession->isAuthorizedForActionsOnResource( - ResourcePattern::forDatabaseName(args.userNames[i].getDB()), - ActionType::viewUser)) { - return Status(ErrorCodes::Unauthorized, - str::stream() << "Not authorized to view users from the " << dbname - << " database"); + invariant(arg.isExact()); + for (const auto& userName : arg.getElements(dbname)) { + if (as->lookupUser(userName)) { + // Can always view users you are logged in as. + continue; } + uassert(ErrorCodes::Unauthorized, + str::stream() << "Not authorized to view users from the " << dbname + << " database", + as->isAuthorizedForActionsOnResource( + ResourcePattern::forDatabaseName(userName.getDB()), ActionType::viewUser)); } } - return Status::OK(); } void checkAuthForTypedCommand(Client* client, const RevokePrivilegesFromRoleCommand& request) { @@ -328,40 +318,32 @@ void checkAuthForTypedCommand(Client* client, const DropAllRolesFromDatabaseComm ActionType::dropRole)); } -Status checkAuthForRolesInfoCommand(Client* client, - const std::string& dbname, - const BSONObj& cmdObj) { - AuthorizationSession* authzSession = AuthorizationSession::get(client); - auth::RolesInfoArgs args; - Status status = auth::parseRolesInfoCommand(cmdObj, dbname, &args); - if (!status.isOK()) { - return status; - } +void checkAuthForTypedCommand(Client* client, const RolesInfoCommand& request) { + const auto& dbname = request.getDbName(); + const auto& arg = request.getCommandParameter(); + auto* as = AuthorizationSession::get(client); - if (args.allForDB) { - if (!authzSession->isAuthorizedForActionsOnResource( - ResourcePattern::forDatabaseName(dbname), ActionType::viewRole)) { - return Status(ErrorCodes::Unauthorized, - str::stream() - << "Not authorized to view roles from the " << dbname << " database"); - } + invariant(!arg.isAllForAllDBs()); + if (arg.isAllOnCurrentDB()) { + uassert(ErrorCodes::Unauthorized, + str::stream() << "Not authorized to view roles from the " << dbname << " database", + as->isAuthorizedForActionsOnResource(ResourcePattern::forDatabaseName(dbname), + ActionType::viewRole)); } else { - for (size_t i = 0; i < args.roleNames.size(); ++i) { - if (authzSession->isAuthenticatedAsUserWithRole(args.roleNames[i])) { + invariant(arg.isExact()); + auto roles = arg.getElements(dbname); + for (const auto& role : roles) { + if (as->isAuthenticatedAsUserWithRole(role)) { continue; // Can always see roles that you are a member of } - if (!authzSession->isAuthorizedForActionsOnResource( - ResourcePattern::forDatabaseName(args.roleNames[i].getDB()), - ActionType::viewRole)) { - return Status(ErrorCodes::Unauthorized, - str::stream() << "Not authorized to view roles from the " - << args.roleNames[i].getDB() << " database"); - } + uassert(ErrorCodes::Unauthorized, + str::stream() << "Not authorized to view roles from the " << role.getDB() + << " database", + as->isAuthorizedForActionsOnResource( + ResourcePattern::forDatabaseName(role.getDB()), ActionType::viewRole)); } } - - return Status::OK(); } Status checkAuthForInvalidateUserCacheCommand(Client* client) { diff --git a/src/mongo/db/commands/user_management_commands_common.h b/src/mongo/db/commands/user_management_commands_common.h index 7bc4caaffe3..3e162039d9f 100644 --- a/src/mongo/db/commands/user_management_commands_common.h +++ b/src/mongo/db/commands/user_management_commands_common.h @@ -93,14 +93,8 @@ void checkAuthForTypedCommand(Client*, const DropUserCommand&); void checkAuthForTypedCommand(Client*, const DropRoleCommand&); void checkAuthForTypedCommand(Client*, const RevokePrivilegesFromRoleCommand&); void checkAuthForTypedCommand(Client*, const DropAllRolesFromDatabaseCommand&); - -Status checkAuthForUsersInfoCommand(Client* client, - const std::string& dbname, - const BSONObj& cmdObj); - -Status checkAuthForRolesInfoCommand(Client* client, - const std::string& dbname, - const BSONObj& cmdObj); +void checkAuthForTypedCommand(Client*, const UsersInfoCommand&); +void checkAuthForTypedCommand(Client*, const RolesInfoCommand&); Status checkAuthForInvalidateUserCacheCommand(Client* client); |