diff options
author | Sara Golemon <sara.golemon@mongodb.com> | 2020-08-07 17:20:40 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2020-08-20 17:20:11 +0000 |
commit | 526230bafa6e5a49f5783507734fba93486c19ae (patch) | |
tree | 0368297ed96c038934c356259c901e3c931643dc /src/mongo/db/auth | |
parent | fbde2a22dab3eaf64c5aec542811be954faaf7e1 (diff) | |
download | mongo-526230bafa6e5a49f5783507734fba93486c19ae.tar.gz |
SERVER-50187 Use AuthzManagerExternalState::roleExists() to simplify role checks
Diffstat (limited to 'src/mongo/db/auth')
13 files changed, 169 insertions, 28 deletions
diff --git a/src/mongo/db/auth/authorization_manager.h b/src/mongo/db/auth/authorization_manager.h index 27fc4c57bb3..01790c106db 100644 --- a/src/mongo/db/auth/authorization_manager.h +++ b/src/mongo/db/auth/authorization_manager.h @@ -214,6 +214,11 @@ public: /** * Delegates method call to the underlying AuthzManagerExternalState. */ + virtual Status rolesExist(OperationContext* opCtx, const std::vector<RoleName>& roleNames) = 0; + + /** + * Delegates method call to the underlying AuthzManagerExternalState. + */ virtual Status getRoleDescription(OperationContext* opCtx, const RoleName& roleName, PrivilegeFormat privilegeFormat, diff --git a/src/mongo/db/auth/authorization_manager_impl.cpp b/src/mongo/db/auth/authorization_manager_impl.cpp index a2ba1112eec..42439d4e791 100644 --- a/src/mongo/db/auth/authorization_manager_impl.cpp +++ b/src/mongo/db/auth/authorization_manager_impl.cpp @@ -416,6 +416,11 @@ Status AuthorizationManagerImpl::getUserDescription(OperationContext* opCtx, return _externalState->getUserDescription(opCtx, UserRequest(userName, boost::none), result); } +Status AuthorizationManagerImpl::rolesExist(OperationContext* opCtx, + const std::vector<RoleName>& roleNames) { + return _externalState->rolesExist(opCtx, roleNames); +} + Status AuthorizationManagerImpl::getRoleDescription(OperationContext* opCtx, const RoleName& roleName, PrivilegeFormat privileges, diff --git a/src/mongo/db/auth/authorization_manager_impl.h b/src/mongo/db/auth/authorization_manager_impl.h index ba2d214d12a..933bbc02e32 100644 --- a/src/mongo/db/auth/authorization_manager_impl.h +++ b/src/mongo/db/auth/authorization_manager_impl.h @@ -72,6 +72,8 @@ public: const UserName& userName, BSONObj* result) override; + Status rolesExist(OperationContext* opCtx, const std::vector<RoleName>& roleNames) override; + Status getRoleDescription(OperationContext* opCtx, const RoleName& roleName, PrivilegeFormat privilegeFormat, diff --git a/src/mongo/db/auth/authz_manager_external_state.cpp b/src/mongo/db/auth/authz_manager_external_state.cpp index 8384fa2b924..5d2eb103f95 100644 --- a/src/mongo/db/auth/authz_manager_external_state.cpp +++ b/src/mongo/db/auth/authz_manager_external_state.cpp @@ -46,4 +46,21 @@ std::unique_ptr<AuthzManagerExternalState> AuthzManagerExternalState::create() { AuthzManagerExternalState::AuthzManagerExternalState() = default; AuthzManagerExternalState::~AuthzManagerExternalState() = default; +Status AuthzManagerExternalState::makeRoleNotFoundStatus( + const stdx::unordered_set<RoleName>& unknownRoles) { + dassert(unknownRoles.size()); + + char delim = ':'; + StringBuilder sb; + sb << "Could not find role"; + if (unknownRoles.size() > 1) { + sb << 's'; + } + for (const auto& unknownRole : unknownRoles) { + sb << delim << ' ' << unknownRole.toString(); + delim = ','; + } + return {ErrorCodes::RoleNotFound, sb.str()}; +} + } // namespace mongo diff --git a/src/mongo/db/auth/authz_manager_external_state.h b/src/mongo/db/auth/authz_manager_external_state.h index 7dde1480d96..867ea493a5f 100644 --- a/src/mongo/db/auth/authz_manager_external_state.h +++ b/src/mongo/db/auth/authz_manager_external_state.h @@ -100,6 +100,11 @@ public: BSONObj* result) = 0; /** + * Checks to see if the named roles exist. + */ + virtual Status rolesExist(OperationContext* opCtx, const std::vector<RoleName>& roleNames) = 0; + + /** * 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 @@ -173,6 +178,11 @@ public: protected: AuthzManagerExternalState(); // This class should never be instantiated directly. + + /** + * Construct a Status about one or more unknown roles. + */ + static Status makeRoleNotFoundStatus(const stdx::unordered_set<RoleName>&); }; } // namespace mongo diff --git a/src/mongo/db/auth/authz_manager_external_state_d.cpp b/src/mongo/db/auth/authz_manager_external_state_d.cpp index 2d143fee899..a5b66fe2622 100644 --- a/src/mongo/db/auth/authz_manager_external_state_d.cpp +++ b/src/mongo/db/auth/authz_manager_external_state_d.cpp @@ -88,6 +88,13 @@ Status AuthzManagerExternalStateMongod::findOne(OperationContext* opCtx, << query); } +bool AuthzManagerExternalStateMongod::hasOne(OperationContext* opCtx, + const NamespaceString& collectionName, + const BSONObj& query) { + AutoGetCollectionForReadCommand ctx(opCtx, collectionName); + return !Helpers::findOne(opCtx, ctx.getCollection(), query, false).isNull(); +} + namespace { std::unique_ptr<AuthzManagerExternalState> authzManagerExternalStateCreateImpl() { diff --git a/src/mongo/db/auth/authz_manager_external_state_d.h b/src/mongo/db/auth/authz_manager_external_state_d.h index 289a7567236..dfb636adba0 100644 --- a/src/mongo/db/auth/authz_manager_external_state_d.h +++ b/src/mongo/db/auth/authz_manager_external_state_d.h @@ -51,17 +51,20 @@ public: virtual ~AuthzManagerExternalStateMongod(); std::unique_ptr<AuthzSessionExternalState> makeAuthzSessionExternalState( - AuthorizationManager* authzManager) override; + AuthorizationManager* authzManager) final; - virtual Status findOne(OperationContext* opCtx, - const NamespaceString& collectionName, - const BSONObj& query, - BSONObj* result); - virtual Status query(OperationContext* opCtx, - const NamespaceString& collectionName, - const BSONObj& query, - const BSONObj& projection, - const std::function<void(const BSONObj&)>& resultProcessor); + Status findOne(OperationContext* opCtx, + const NamespaceString& collectionName, + const BSONObj& query, + BSONObj* result) final; + bool hasOne(OperationContext* opCtx, + const NamespaceString& collectionName, + const BSONObj& query) final; + Status query(OperationContext* opCtx, + const NamespaceString& collectionName, + const BSONObj& query, + const BSONObj& projection, + const std::function<void(const BSONObj&)>& resultProcessor) final; }; } // namespace mongo diff --git a/src/mongo/db/auth/authz_manager_external_state_local.cpp b/src/mongo/db/auth/authz_manager_external_state_local.cpp index 89ad8fbe7a5..e530074b5a4 100644 --- a/src/mongo/db/auth/authz_manager_external_state_local.cpp +++ b/src/mongo/db/auth/authz_manager_external_state_local.cpp @@ -293,6 +293,25 @@ Status AuthzManagerExternalStateLocal::_getUserDocument(OperationContext* opCtx, return status; } +Status AuthzManagerExternalStateLocal::rolesExist(OperationContext* opCtx, + const std::vector<RoleName>& roleNames) { + // Perform DB queries for user-defined roles (skipping builtin roles). + stdx::unordered_set<RoleName> unknownRoles; + for (const auto& roleName : roleNames) { + if (!RoleGraph::isBuiltinRole(roleName) && + !hasOne(opCtx, AuthorizationManager::rolesCollectionNamespace, roleName.toBSON())) { + unknownRoles.insert(roleName); + } + } + + // If anything remains, raise it as an unknown role error. + if (!unknownRoles.empty()) { + return makeRoleNotFoundStatus(unknownRoles); + } + + return Status::OK(); +} + Status AuthzManagerExternalStateLocal::getRoleDescription( OperationContext* opCtx, const RoleName& roleName, 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 bd01781fc97..0832d9ea4fa 100644 --- a/src/mongo/db/auth/authz_manager_external_state_local.h +++ b/src/mongo/db/auth/authz_manager_external_state_local.h @@ -62,6 +62,7 @@ public: Status getUserDescription(OperationContext* opCtx, 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, @@ -102,6 +103,13 @@ public: BSONObj* result) = 0; /** + * Checks for the existance of a document matching "query" in "collectionName". + */ + virtual bool hasOne(OperationContext* opCtx, + const NamespaceString& collectionName, + const BSONObj& query) = 0; + + /** * Finds all documents matching "query" in "collectionName". For each document returned, * calls the function resultProcessor on it. */ diff --git a/src/mongo/db/auth/authz_manager_external_state_mock.cpp b/src/mongo/db/auth/authz_manager_external_state_mock.cpp index 1e2c22934b3..57aee1183ea 100644 --- a/src/mongo/db/auth/authz_manager_external_state_mock.cpp +++ b/src/mongo/db/auth/authz_manager_external_state_mock.cpp @@ -127,6 +127,14 @@ Status AuthzManagerExternalStateMock::findOne(OperationContext* opCtx, return Status::OK(); } + +bool AuthzManagerExternalStateMock::hasOne(OperationContext* opCtx, + const NamespaceString& collectionName, + const BSONObj& query) { + BSONObjCollection::iterator iter; + return _findOneIter(opCtx, collectionName, query, &iter).isOK(); +} + Status AuthzManagerExternalStateMock::query( OperationContext* opCtx, const NamespaceString& collectionName, diff --git a/src/mongo/db/auth/authz_manager_external_state_mock.h b/src/mongo/db/auth/authz_manager_external_state_mock.h index 670c73c924f..cd0a5793c30 100644 --- a/src/mongo/db/auth/authz_manager_external_state_mock.h +++ b/src/mongo/db/auth/authz_manager_external_state_mock.h @@ -61,16 +61,21 @@ public: std::unique_ptr<AuthzSessionExternalState> makeAuthzSessionExternalState( AuthorizationManager* authzManager) override; - virtual Status findOne(OperationContext* opCtx, - const NamespaceString& collectionName, - const BSONObj& query, - BSONObj* result); - - virtual Status query(OperationContext* opCtx, - const NamespaceString& collectionName, - const BSONObj& query, - const BSONObj& projection, // Currently unused in mock - const std::function<void(const BSONObj&)>& resultProcessor); + Status findOne(OperationContext* opCtx, + const NamespaceString& collectionName, + const BSONObj& query, + BSONObj* result) override; + + bool hasOne(OperationContext* opCtx, + const NamespaceString& collectionName, + const BSONObj& query) override; + + + Status query(OperationContext* opCtx, + const NamespaceString& collectionName, + const BSONObj& query, + const BSONObj& projection, // Currently unused in mock + const std::function<void(const BSONObj&)>& resultProcessor) override; /** * Inserts the given user object into the "admin" database. 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 95e906a8acc..d43be5722ce 100644 --- a/src/mongo/db/auth/authz_manager_external_state_s.cpp +++ b/src/mongo/db/auth/authz_manager_external_state_s.cpp @@ -192,6 +192,57 @@ Status AuthzManagerExternalStateMongos::getUserDescription(OperationContext* opC } } +Status AuthzManagerExternalStateMongos::rolesExist(OperationContext* opCtx, + const std::vector<RoleName>& roleNames) try { + // Marshall role names into a set before querying so that we don't get a false-negative + // from repeated roles only providing one result at the end. + stdx::unordered_set<RoleName> roleNameSet(roleNames.cbegin(), roleNames.cend()); + + BSONObjBuilder rolesInfoCmd; + + { + BSONArrayBuilder rolesArray(rolesInfoCmd.subarrayStart("rolesInfo")); + for (const auto& roleName : roleNameSet) { + roleName.serializeToBSON(&rolesArray); + } + rolesArray.doneFast(); + } + + BSONObjBuilder resultBuilder; + if (!Grid::get(opCtx)->catalogClient()->runUserManagementReadCommand( + opCtx, "admin", rolesInfoCmd.obj(), &resultBuilder)) { + return {ErrorCodes::OperationFailed, "Failed running rolesInfo command on mongod"}; + } + + auto result = resultBuilder.obj(); + auto cmdStatus = getStatusFromCommandResult(result); + if (!cmdStatus.isOK()) { + return {cmdStatus.code(), + str::stream() << "Failed running rolesInfo command on mongod: " + << cmdStatus.reason()}; + } + + auto roles = result["roles"]; + if (roles.type() != Array) { + return {ErrorCodes::OperationFailed, + "Received invalid response from rolesInfo command on mongod"}; + } + + if (static_cast<std::size_t>(roles.Obj().nFields()) != roleNameSet.size()) { + // One or more missing roles, cross out the ones that do exist, and return error. + for (const auto& roleObj : roles.Obj()) { + auto roleName = RoleName::parseFromBSON(roleObj); + roleNameSet.erase(roleName); + } + + return makeRoleNotFoundStatus(roleNameSet); + } + + return Status::OK(); +} catch (const AssertionException& ex) { + return ex.toStatus(); +} + Status AuthzManagerExternalStateMongos::getRoleDescription( OperationContext* opCtx, const RoleName& roleName, 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 939a2ca1cb2..4d310c6aa47 100644 --- a/src/mongo/db/auth/authz_manager_external_state_s.h +++ b/src/mongo/db/auth/authz_manager_external_state_s.h @@ -49,33 +49,34 @@ class AuthzManagerExternalStateMongos : public AuthzManagerExternalState { public: AuthzManagerExternalStateMongos(); - ~AuthzManagerExternalStateMongos() override; + ~AuthzManagerExternalStateMongos() final; - Status initialize(OperationContext* opCtx) override; + Status initialize(OperationContext* opCtx) final; std::unique_ptr<AuthzSessionExternalState> makeAuthzSessionExternalState( - AuthorizationManager* authzManager) override; + AuthorizationManager* authzManager) final; Status getStoredAuthorizationVersion(OperationContext* opCtx, int* outVersion) override; + Status rolesExist(OperationContext* opCtx, const std::vector<RoleName>& roleNames) final; Status getUserDescription(OperationContext* opCtx, const UserRequest& user, - BSONObj* result) override; + BSONObj* result) final; Status getRoleDescription(OperationContext* opCtx, const RoleName& roleName, PrivilegeFormat showPrivileges, AuthenticationRestrictionsFormat, - BSONObj* result) override; + BSONObj* result) final; Status getRolesDescription(OperationContext* opCtx, const std::vector<RoleName>& roles, PrivilegeFormat showPrivileges, AuthenticationRestrictionsFormat, - BSONObj* result) override; + BSONObj* result) final; Status getRoleDescriptionsForDB(OperationContext* opCtx, StringData dbname, PrivilegeFormat showPrivileges, AuthenticationRestrictionsFormat, bool showBuiltinRoles, - std::vector<BSONObj>* result) override; + std::vector<BSONObj>* result) final; - bool hasAnyPrivilegeDocuments(OperationContext* opCtx) override; + bool hasAnyPrivilegeDocuments(OperationContext* opCtx) final; }; } // namespace mongo |