diff options
author | Sara Golemon <sara.golemon@mongodb.com> | 2017-07-18 18:04:29 -0400 |
---|---|---|
committer | Sara Golemon <sara.golemon@mongodb.com> | 2017-07-24 21:17:06 -0400 |
commit | d8094ad9be285cb5bf588faff20af785bb76ce4b (patch) | |
tree | 1e14c49054f4c948595a9becbea7fcfa06f7b7f8 /src/mongo/db/auth | |
parent | f22a10013a0aa26a4ad776e0f69e0ab5d33c5f86 (diff) | |
download | mongo-d8094ad9be285cb5bf588faff20af785bb76ce4b.tar.gz |
SERVER-29183 Add restriction support to rolesInfo
Diffstat (limited to 'src/mongo/db/auth')
-rw-r--r-- | src/mongo/db/auth/authorization_manager.cpp | 9 | ||||
-rw-r--r-- | src/mongo/db/auth/authorization_manager.h | 19 | ||||
-rw-r--r-- | src/mongo/db/auth/authz_manager_external_state.h | 7 | ||||
-rw-r--r-- | src/mongo/db/auth/authz_manager_external_state_local.cpp | 184 | ||||
-rw-r--r-- | src/mongo/db/auth/authz_manager_external_state_local.h | 42 | ||||
-rw-r--r-- | src/mongo/db/auth/authz_manager_external_state_s.cpp | 50 | ||||
-rw-r--r-- | src/mongo/db/auth/authz_manager_external_state_s.h | 43 | ||||
-rw-r--r-- | src/mongo/db/auth/user_document_parser.cpp | 54 | ||||
-rw-r--r-- | src/mongo/db/auth/user_management_commands_parser.cpp | 19 | ||||
-rw-r--r-- | src/mongo/db/auth/user_management_commands_parser.h | 17 |
10 files changed, 310 insertions, 134 deletions
diff --git a/src/mongo/db/auth/authorization_manager.cpp b/src/mongo/db/auth/authorization_manager.cpp index 51abb758867..5f72d9c04cd 100644 --- a/src/mongo/db/auth/authorization_manager.cpp +++ b/src/mongo/db/auth/authorization_manager.cpp @@ -456,25 +456,28 @@ Status AuthorizationManager::getUserDescription(OperationContext* opCtx, Status AuthorizationManager::getRoleDescription(OperationContext* opCtx, const RoleName& roleName, PrivilegeFormat privileges, + AuthenticationRestrictionsFormat restrictions, BSONObj* result) { - return _externalState->getRoleDescription(opCtx, roleName, privileges, result); + return _externalState->getRoleDescription(opCtx, roleName, privileges, restrictions, result); } Status AuthorizationManager::getRolesDescription(OperationContext* opCtx, const std::vector<RoleName>& roleName, PrivilegeFormat privileges, + AuthenticationRestrictionsFormat restrictions, BSONObj* result) { - return _externalState->getRolesDescription(opCtx, roleName, privileges, result); + return _externalState->getRolesDescription(opCtx, roleName, privileges, restrictions, result); } Status AuthorizationManager::getRoleDescriptionsForDB(OperationContext* opCtx, const std::string dbname, PrivilegeFormat privileges, + AuthenticationRestrictionsFormat restrictions, bool showBuiltinRoles, vector<BSONObj>* result) { return _externalState->getRoleDescriptionsForDB( - opCtx, dbname, privileges, showBuiltinRoles, result); + opCtx, dbname, privileges, restrictions, showBuiltinRoles, result); } Status AuthorizationManager::acquireUserToRefreshSessionCache(OperationContext* opCtx, diff --git a/src/mongo/db/auth/authorization_manager.h b/src/mongo/db/auth/authorization_manager.h index ab70e324079..e8840838e11 100644 --- a/src/mongo/db/auth/authorization_manager.h +++ b/src/mongo/db/auth/authorization_manager.h @@ -68,6 +68,14 @@ struct AuthInfo { extern AuthInfo internalSecurity; // set at startup and not changed after initialization. /** + * How user management functions should structure the BSON representation of privileges and roles. + */ +enum class AuthenticationRestrictionsFormat { + kOmit, // AuthenticationRestrictions should not be included in the BSON representation. + kShow, // AuthenticationRestrictions should be included in the BSON representation. +}; + +/** * Contains server/cluster-wide information about Authorization. */ class AuthorizationManager { @@ -239,14 +247,24 @@ public: Status getRoleDescription(OperationContext* opCtx, const RoleName& roleName, PrivilegeFormat privilegeFormat, + AuthenticationRestrictionsFormat, BSONObj* result); /** + * Convenience wrapper for getRoleDescription() defaulting formats to kOmit. + */ + Status getRoleDescription(OperationContext* ctx, const RoleName& roleName, BSONObj* result) { + return getRoleDescription( + ctx, roleName, PrivilegeFormat::kOmit, AuthenticationRestrictionsFormat::kOmit, result); + } + + /** * Delegates method call to the underlying AuthzManagerExternalState. */ Status getRolesDescription(OperationContext* opCtx, const std::vector<RoleName>& roleName, PrivilegeFormat privilegeFormat, + AuthenticationRestrictionsFormat, BSONObj* result); /** @@ -255,6 +273,7 @@ public: Status getRoleDescriptionsForDB(OperationContext* opCtx, const std::string dbname, PrivilegeFormat privilegeFormat, + AuthenticationRestrictionsFormat, bool showBuiltinRoles, std::vector<BSONObj>* result); diff --git a/src/mongo/db/auth/authz_manager_external_state.h b/src/mongo/db/auth/authz_manager_external_state.h index f63e05b818f..51d2e7a4808 100644 --- a/src/mongo/db/auth/authz_manager_external_state.h +++ b/src/mongo/db/auth/authz_manager_external_state.h @@ -34,6 +34,7 @@ #include "mongo/base/disallow_copying.h" #include "mongo/base/status.h" +#include "mongo/db/auth/authorization_manager.h" #include "mongo/db/auth/privilege_format.h" #include "mongo/db/auth/role_name.h" #include "mongo/db/auth/user.h" @@ -43,7 +44,6 @@ namespace mongo { -class AuthorizationManager; class AuthzSessionExternalState; class OperationContext; @@ -112,6 +112,7 @@ public: virtual Status getRoleDescription(OperationContext* opCtx, const RoleName& roleName, PrivilegeFormat showPrivileges, + AuthenticationRestrictionsFormat, BSONObj* result) = 0; /** @@ -130,6 +131,7 @@ public: virtual Status getRolesDescription(OperationContext* opCtx, const std::vector<RoleName>& roles, PrivilegeFormat showPrivileges, + AuthenticationRestrictionsFormat, BSONObj* result) = 0; /** @@ -145,8 +147,9 @@ public: * contain a "warnings" array, with std::string messages describing inconsistencies. */ virtual Status getRoleDescriptionsForDB(OperationContext* opCtx, - const std::string dbname, + const std::string& dbname, PrivilegeFormat showPrivileges, + AuthenticationRestrictionsFormat, bool showBuiltinRoles, std::vector<BSONObj>* result) = 0; 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 ad4236b298b..58b13d7099b 100644 --- a/src/mongo/db/auth/authz_manager_external_state_local.cpp +++ b/src/mongo/db/auth/authz_manager_external_state_local.cpp @@ -133,6 +133,14 @@ void addPrivilegeObjectsOrWarningsToArrayElement(mutablebson::Element privileges } } } + +void addAuthenticationRestrictionObjectsToArrayElement( + mutablebson::Element restrictionsElement, + const std::vector<SharedRestrictionDocument>& restrictions) { + for (const auto& r : restrictions) { + fassert(40560, restrictionsElement.appendArray("", r->toBSON())); + } +} } // namespace bool AuthzManagerExternalStateLocal::hasAnyPrivilegeDocuments(OperationContext* opCtx) { @@ -188,6 +196,21 @@ Status AuthzManagerExternalStateLocal::getUserDescription(OperationContext* opCt resolveUserRoles(&resultDoc, directRoles); *result = resultDoc.getObject(); + const auto isNonEmptyArray = [](const BSONObj& doc, StringData element) { + const auto& e = doc[element]; + return !e.eoo() && (e.type() == Array) && !e.Obj().isEmpty(); + }; + + if ((isNonEmptyArray(*result, "authenticationRestrictions") || + isNonEmptyArray(*result, "inheritedAuthenticationRestrictions")) && + serverGlobalParams.featureCompatibility.version.load() < + ServerGlobalParams::FeatureCompatibility::Version::k36) { + // Mongos isn't able to evaluate whether documents are valid under the current + // featureCompatibilityVersion. We must make the decision before it sees them. + return Status(ErrorCodes::UnsupportedFormat, + "'authenticationRestrictions' requires 3.6 feature compatibility version"); + } + return Status::OK(); } @@ -195,45 +218,67 @@ void AuthzManagerExternalStateLocal::resolveUserRoles(mutablebson::Document* use const std::vector<RoleName>& directRoles) { unordered_set<RoleName> indirectRoles; PrivilegeVector allPrivileges; - bool isRoleGraphInconsistent; + std::vector<SharedRestrictionDocument> allAuthenticationRestrictions; + bool isRoleGraphConsistent = false; + { stdx::lock_guard<stdx::mutex> lk(_roleGraphMutex); - isRoleGraphInconsistent = _roleGraphState == roleGraphStateConsistent; - for (size_t i = 0; i < directRoles.size(); ++i) { - const RoleName& role(directRoles[i]); + isRoleGraphConsistent = _roleGraphState == roleGraphStateConsistent; + for (const auto& role : directRoles) { indirectRoles.insert(role); - if (isRoleGraphInconsistent) { + if (isRoleGraphConsistent) { for (RoleNameIterator subordinates = _roleGraph.getIndirectSubordinates(role); subordinates.more(); subordinates.next()) { indirectRoles.insert(subordinates.get()); } } - const PrivilegeVector& rolePrivileges(isRoleGraphInconsistent - ? _roleGraph.getAllPrivileges(role) - : _roleGraph.getDirectPrivileges(role)); - for (PrivilegeVector::const_iterator priv = rolePrivileges.begin(), - end = rolePrivileges.end(); - priv != end; - ++priv) { - Privilege::addPrivilegeToPrivilegeVector(&allPrivileges, *priv); + + const auto& currentPrivileges = isRoleGraphConsistent + ? _roleGraph.getAllPrivileges(role) + : _roleGraph.getDirectPrivileges(role); + for (const auto& priv : currentPrivileges) { + Privilege::addPrivilegeToPrivilegeVector(&allPrivileges, priv); + } + + if (isRoleGraphConsistent) { + const auto& currentAuthenticationRestrictions = + _roleGraph.getAllAuthenticationRestrictions(role); + allAuthenticationRestrictions.insert(allAuthenticationRestrictions.end(), + currentAuthenticationRestrictions.begin(), + currentAuthenticationRestrictions.end()); + } else { + const auto& dar = _roleGraph.getDirectAuthenticationRestrictions(role); + if (dar.get()) { + allAuthenticationRestrictions.push_back(dar); + } } } } - mutablebson::Element inheritedRolesElement = userDoc->makeElementArray("inheritedRoles"); - mutablebson::Element privilegesElement = userDoc->makeElementArray("inheritedPrivileges"); - mutablebson::Element warningsElement = userDoc->makeElementArray("warnings"); + auto warningsElement = userDoc->makeElementArray("warnings"); + + auto inheritedRolesElement = userDoc->makeElementArray("inheritedRoles"); fassert(17159, userDoc->root().pushBack(inheritedRolesElement)); + addRoleNameObjectsToArrayElement(inheritedRolesElement, + makeRoleNameIteratorForContainer(indirectRoles)); + + auto privilegesElement = userDoc->makeElementArray("inheritedPrivileges"); fassert(17158, userDoc->root().pushBack(privilegesElement)); - if (!isRoleGraphInconsistent) { + addPrivilegeObjectsOrWarningsToArrayElement(privilegesElement, warningsElement, allPrivileges); + + auto authenticationRestrictionsElement = + userDoc->makeElementArray("inheritedAuthenticationRestrictions"); + fassert(40558, userDoc->root().pushBack(authenticationRestrictionsElement)); + addAuthenticationRestrictionObjectsToArrayElement(authenticationRestrictionsElement, + allAuthenticationRestrictions); + + if (!isRoleGraphConsistent) { fassert(17160, warningsElement.appendString( "", "Role graph inconsistent, only direct privileges available.")); } - addRoleNameObjectsToArrayElement(inheritedRolesElement, - makeRoleNameIteratorForContainer(indirectRoles)); - addPrivilegeObjectsOrWarningsToArrayElement(privilegesElement, warningsElement, allPrivileges); + if (warningsElement.hasChildren()) { fassert(17161, userDoc->root().pushBack(warningsElement)); } @@ -249,6 +294,7 @@ Status AuthzManagerExternalStateLocal::_getUserDocument(OperationContext* opCtx, << AuthorizationManager::USER_DB_FIELD_NAME << userName.getDB()), userDoc); + if (status == ErrorCodes::NoMatchingDocument) { status = Status(ErrorCodes::UserNotFound, @@ -264,10 +310,12 @@ Status AuthzManagerExternalStateLocal::_getUserDocument(OperationContext* opCtx, return status; } -Status AuthzManagerExternalStateLocal::getRoleDescription(OperationContext* opCtx, - const RoleName& roleName, - PrivilegeFormat showPrivileges, - BSONObj* result) { +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"); @@ -279,13 +327,15 @@ Status AuthzManagerExternalStateLocal::getRoleDescription(OperationContext* opCt return Status::OK(); } stdx::lock_guard<stdx::mutex> lk(_roleGraphMutex); - return _getRoleDescription_inlock(roleName, showPrivileges, result); + return _getRoleDescription_inlock(roleName, showPrivileges, showRestrictions, result); } -Status AuthzManagerExternalStateLocal::getRolesDescription(OperationContext* opCtx, - const std::vector<RoleName>& roles, - PrivilegeFormat showPrivileges, - BSONObj* 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"); @@ -300,7 +350,8 @@ Status AuthzManagerExternalStateLocal::getRolesDescription(OperationContext* opC BSONArrayBuilder resultBuilder; for (const RoleName& role : roles) { BSONObj roleDoc; - Status status = _getRoleDescription_inlock(role, showPrivileges, &roleDoc); + Status status = + _getRoleDescription_inlock(role, showPrivileges, showRestrictions, &roleDoc); if (!status.isOK()) { if (status.code() == ErrorCodes::RoleNotFound) { continue; @@ -313,9 +364,11 @@ Status AuthzManagerExternalStateLocal::getRolesDescription(OperationContext* opC return Status::OK(); } -Status AuthzManagerExternalStateLocal::_getRoleDescription_inlock(const RoleName& roleName, - PrivilegeFormat showPrivileges, - BSONObj* result) { +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()); @@ -327,23 +380,39 @@ Status AuthzManagerExternalStateLocal::_getRoleDescription_inlock(const RoleName 17163, resultDoc.root().appendString(AuthorizationManager::ROLE_DB_FIELD_NAME, roleName.getDB())); fassert(17267, resultDoc.root().appendBool("isBuiltin", _roleGraph.isBuiltinRole(roleName))); - mutablebson::Element rolesElement = resultDoc.makeElementArray("roles"); + + auto warningsElement = resultDoc.makeElementArray("warnings"); + + auto rolesElement = resultDoc.makeElementArray("roles"); fassert(17164, resultDoc.root().pushBack(rolesElement)); - mutablebson::Element inheritedRolesElement = resultDoc.makeElementArray("inheritedRoles"); + addRoleNameObjectsToArrayElement(rolesElement, _roleGraph.getDirectSubordinates(roleName)); + + auto inheritedRolesElement = resultDoc.makeElementArray("inheritedRoles"); fassert(17165, resultDoc.root().pushBack(inheritedRolesElement)); - mutablebson::Element privilegesElement = resultDoc.makeElementArray("privileges"); - mutablebson::Element inheritedPrivilegesElement = - resultDoc.makeElementArray("inheritedPrivileges"); + + auto privilegesElement = resultDoc.makeElementArray("privileges"); if (showPrivileges == PrivilegeFormat::kShowSeparate) { fassert(17166, resultDoc.root().pushBack(privilegesElement)); } - mutablebson::Element warningsElement = resultDoc.makeElementArray("warnings"); - addRoleNameObjectsToArrayElement(rolesElement, _roleGraph.getDirectSubordinates(roleName)); + 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())); + } + } + if (_roleGraphState == roleGraphStateConsistent) { addRoleNameObjectsToArrayElement(inheritedRolesElement, _roleGraph.getIndirectSubordinates(roleName)); + if (showPrivileges == PrivilegeFormat::kShowSeparate) { + auto inheritedPrivilegesElement = resultDoc.makeElementArray("inheritedPrivileges"); addPrivilegeObjectsOrWarningsToArrayElement( privilegesElement, warningsElement, _roleGraph.getDirectPrivileges(roleName)); @@ -352,13 +421,27 @@ Status AuthzManagerExternalStateLocal::_getRoleDescription_inlock(const RoleName fassert(17323, resultDoc.root().pushBack(inheritedPrivilegesElement)); } + + if (showRestrictions == AuthenticationRestrictionsFormat::kShow) { + auto indirectAuthenticationRestrictionsElement = + resultDoc.makeElementArray("indirectAuthenticationRestrictions"); + fassert(40563, resultDoc.root().pushBack(indirectAuthenticationRestrictionsElement)); + + for (const auto& restrictions : _roleGraph.getAllAuthenticationRestrictions(roleName)) { + fassert(40562, + indirectAuthenticationRestrictionsElement.appendArray( + "", restrictions->toBSON())); + } + } } else if (showPrivileges == PrivilegeFormat::kShowSeparate) { - warningsElement - .appendString("", "Role graph state inconsistent; only direct privileges available.") - .transitional_ignore(); 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)); } @@ -366,11 +449,13 @@ Status AuthzManagerExternalStateLocal::_getRoleDescription_inlock(const RoleName return Status::OK(); } -Status AuthzManagerExternalStateLocal::getRoleDescriptionsForDB(OperationContext* opCtx, - const std::string dbname, - PrivilegeFormat showPrivileges, - bool showBuiltinRoles, - vector<BSONObj>* result) { +Status AuthzManagerExternalStateLocal::getRoleDescriptionsForDB( + OperationContext* opCtx, + const std::string& dbname, + PrivilegeFormat showPrivileges, + AuthenticationRestrictionsFormat showRestrictions, + bool showBuiltinRoles, + vector<BSONObj>* result) { if (showPrivileges == PrivilegeFormat::kShowAsUserFragment) { return Status(ErrorCodes::IllegalOperation, "Cannot get user fragment for all roles in a database"); @@ -382,7 +467,8 @@ Status AuthzManagerExternalStateLocal::getRoleDescriptionsForDB(OperationContext continue; } BSONObj roleDoc; - Status status = _getRoleDescription_inlock(it.get(), showPrivileges, &roleDoc); + Status status = + _getRoleDescription_inlock(it.get(), showPrivileges, showRestrictions, &roleDoc); if (!status.isOK()) { return status; } 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 d33b8059084..292eae07f00 100644 --- a/src/mongo/db/auth/authz_manager_external_state_local.h +++ b/src/mongo/db/auth/authz_manager_external_state_local.h @@ -55,25 +55,28 @@ class AuthzManagerExternalStateLocal : public AuthzManagerExternalState { public: virtual ~AuthzManagerExternalStateLocal() = default; - virtual Status initialize(OperationContext* opCtx); - - virtual Status getStoredAuthorizationVersion(OperationContext* opCtx, int* outVersion); - virtual Status getUserDescription(OperationContext* opCtx, - const UserName& userName, - BSONObj* result); - virtual Status getRoleDescription(OperationContext* opCtx, - const RoleName& roleName, - PrivilegeFormat showPrivileges, - BSONObj* result); - virtual Status getRolesDescription(OperationContext* opCtx, - const std::vector<RoleName>& roles, - PrivilegeFormat showPrivileges, - BSONObj* result); - virtual Status getRoleDescriptionsForDB(OperationContext* opCtx, - const std::string dbname, - PrivilegeFormat showPrivileges, - bool showBuiltinRoles, - std::vector<BSONObj>* result); + Status initialize(OperationContext* opCtx) override; + + Status getStoredAuthorizationVersion(OperationContext* opCtx, int* outVersion) override; + Status getUserDescription(OperationContext* opCtx, + const UserName& userName, + BSONObj* result) override; + Status getRoleDescription(OperationContext* opCtx, + const RoleName& roleName, + PrivilegeFormat showPrivileges, + AuthenticationRestrictionsFormat, + BSONObj* result) override; + Status getRolesDescription(OperationContext* opCtx, + const std::vector<RoleName>& roles, + PrivilegeFormat showPrivileges, + AuthenticationRestrictionsFormat, + BSONObj* result) override; + Status getRoleDescriptionsForDB(OperationContext* opCtx, + const std::string& dbname, + PrivilegeFormat showPrivileges, + AuthenticationRestrictionsFormat, + bool showBuiltinRoles, + std::vector<BSONObj>* result) override; bool hasAnyPrivilegeDocuments(OperationContext* opCtx) override; @@ -139,6 +142,7 @@ private: Status _getRoleDescription_inlock(const RoleName& roleName, PrivilegeFormat showPrivileges, + AuthenticationRestrictionsFormat showRestrictions, BSONObj* result); /** * Eventually consistent, in-memory representation of all roles in the system (both 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 d941b3f0557..852e66fda7a 100644 --- a/src/mongo/db/auth/authz_manager_external_state_s.cpp +++ b/src/mongo/db/auth/authz_manager_external_state_s.cpp @@ -67,13 +67,15 @@ std::string rolesFieldName(PrivilegeFormat showPrivileges) { /** * Attches a string representation of a PrivilegeFormat to the provided BSONObjBuilder. */ -void addShowPrivilegesToBuilder(BSONObjBuilder* builder, PrivilegeFormat showPrivileges) { - if (showPrivileges == PrivilegeFormat::kShowSeparate) { - builder->append("showPrivileges", true); - } else if (showPrivileges == PrivilegeFormat::kShowAsUserFragment) { +void addShowToBuilder(BSONObjBuilder* builder, + PrivilegeFormat showPrivileges, + AuthenticationRestrictionsFormat showRestrictions) { + if (showPrivileges == PrivilegeFormat::kShowAsUserFragment) { builder->append("showPrivileges", "asUserfragment"); } else { - builder->append("showPrivileges", false); + builder->append("showPrivileges", showPrivileges == PrivilegeFormat::kShowSeparate); + builder->append("showAuthenticationRestrictions", + showRestrictions == AuthenticationRestrictionsFormat::kShow); } } @@ -204,17 +206,19 @@ Status AuthzManagerExternalStateMongos::getUserDescription(OperationContext* opC } } -Status AuthzManagerExternalStateMongos::getRoleDescription(OperationContext* opCtx, - const RoleName& roleName, - PrivilegeFormat showPrivileges, - BSONObj* result) { +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()))); - addShowPrivilegesToBuilder(&rolesInfoCmd, showPrivileges); + addShowToBuilder(&rolesInfoCmd, showPrivileges, showRestrictions); BSONObjBuilder builder; const bool ok = Grid::get(opCtx)->catalogClient()->runUserManagementReadCommand( @@ -239,10 +243,12 @@ Status AuthzManagerExternalStateMongos::getRoleDescription(OperationContext* opC *result = foundRoles[0].Obj().getOwned(); return Status::OK(); } -Status AuthzManagerExternalStateMongos::getRolesDescription(OperationContext* opCtx, - const std::vector<RoleName>& roles, - PrivilegeFormat showPrivileges, - BSONObj* result) { +Status AuthzManagerExternalStateMongos::getRolesDescription( + OperationContext* opCtx, + const std::vector<RoleName>& roles, + PrivilegeFormat showPrivileges, + AuthenticationRestrictionsFormat showRestrictions, + BSONObj* result) { BSONArrayBuilder rolesInfoCmdArray; for (const RoleName& roleName : roles) { @@ -254,7 +260,7 @@ Status AuthzManagerExternalStateMongos::getRolesDescription(OperationContext* op BSONObjBuilder rolesInfoCmd; rolesInfoCmd.append("rolesInfo", rolesInfoCmdArray.arr()); - addShowPrivilegesToBuilder(&rolesInfoCmd, showPrivileges); + addShowToBuilder(&rolesInfoCmd, showPrivileges, showRestrictions); BSONObjBuilder builder; const bool ok = Grid::get(opCtx)->catalogClient()->runUserManagementReadCommand( @@ -273,14 +279,16 @@ Status AuthzManagerExternalStateMongos::getRolesDescription(OperationContext* op return Status::OK(); } -Status AuthzManagerExternalStateMongos::getRoleDescriptionsForDB(OperationContext* opCtx, - const std::string dbname, - PrivilegeFormat showPrivileges, - bool showBuiltinRoles, - std::vector<BSONObj>* result) { +Status AuthzManagerExternalStateMongos::getRoleDescriptionsForDB( + OperationContext* opCtx, + const std::string& dbname, + PrivilegeFormat showPrivileges, + AuthenticationRestrictionsFormat showRestrictions, + bool showBuiltinRoles, + std::vector<BSONObj>* result) { BSONObjBuilder rolesInfoCmd; rolesInfoCmd << "rolesInfo" << 1 << "showBuiltinRoles" << showBuiltinRoles; - addShowPrivilegesToBuilder(&rolesInfoCmd, showPrivileges); + addShowToBuilder(&rolesInfoCmd, showPrivileges, showRestrictions); BSONObjBuilder builder; const bool ok = Grid::get(opCtx)->catalogClient()->runUserManagementReadCommand( 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 3ae98503aad..5cfff629074 100644 --- a/src/mongo/db/auth/authz_manager_external_state_s.h +++ b/src/mongo/db/auth/authz_manager_external_state_s.h @@ -33,7 +33,9 @@ #include "mongo/base/disallow_copying.h" #include "mongo/base/status.h" +#include "mongo/db/auth/authorization_manager.h" #include "mongo/db/auth/authz_manager_external_state.h" +#include "mongo/db/auth/privilege_format.h" #include "mongo/db/auth/user_name.h" #include "mongo/stdx/functional.h" @@ -48,28 +50,31 @@ class AuthzManagerExternalStateMongos : public AuthzManagerExternalState { public: AuthzManagerExternalStateMongos(); - virtual ~AuthzManagerExternalStateMongos(); + ~AuthzManagerExternalStateMongos() override; - virtual Status initialize(OperationContext* opCtx); + Status initialize(OperationContext* opCtx) override; std::unique_ptr<AuthzSessionExternalState> makeAuthzSessionExternalState( AuthorizationManager* authzManager) override; - virtual Status getStoredAuthorizationVersion(OperationContext* opCtx, int* outVersion); - virtual Status getUserDescription(OperationContext* opCtx, - const UserName& userName, - BSONObj* result); - virtual Status getRoleDescription(OperationContext* opCtx, - const RoleName& roleName, - PrivilegeFormat showPrivileges, - BSONObj* result); - virtual Status getRolesDescription(OperationContext* opCtx, - const std::vector<RoleName>& roles, - PrivilegeFormat showPrivileges, - BSONObj* result); - virtual Status getRoleDescriptionsForDB(OperationContext* opCtx, - const std::string dbname, - PrivilegeFormat showPrivileges, - bool showBuiltinRoles, - std::vector<BSONObj>* result); + Status getStoredAuthorizationVersion(OperationContext* opCtx, int* outVersion) override; + Status getUserDescription(OperationContext* opCtx, + const UserName& userName, + BSONObj* result) override; + Status getRoleDescription(OperationContext* opCtx, + const RoleName& roleName, + PrivilegeFormat showPrivileges, + AuthenticationRestrictionsFormat, + BSONObj* result) override; + Status getRolesDescription(OperationContext* opCtx, + const std::vector<RoleName>& roles, + PrivilegeFormat showPrivileges, + AuthenticationRestrictionsFormat, + BSONObj* result) override; + Status getRoleDescriptionsForDB(OperationContext* opCtx, + const std::string& dbname, + PrivilegeFormat showPrivileges, + AuthenticationRestrictionsFormat, + bool showBuiltinRoles, + std::vector<BSONObj>* result) override; bool hasAnyPrivilegeDocuments(OperationContext* opCtx) override; }; diff --git a/src/mongo/db/auth/user_document_parser.cpp b/src/mongo/db/auth/user_document_parser.cpp index 9ac0fd3089a..f1325910d2b 100644 --- a/src/mongo/db/auth/user_document_parser.cpp +++ b/src/mongo/db/auth/user_document_parser.cpp @@ -60,6 +60,8 @@ const std::string MONGODB_CR_CREDENTIAL_FIELD_NAME = "MONGODB-CR"; const std::string SCRAM_CREDENTIAL_FIELD_NAME = "SCRAM-SHA-1"; const std::string MONGODB_EXTERNAL_CREDENTIAL_FIELD_NAME = "external"; constexpr StringData AUTHENTICATION_RESTRICTIONS_FIELD_NAME = "authenticationRestrictions"_sd; +constexpr StringData INHERITED_AUTHENTICATION_RESTRICTIONS_FIELD_NAME = + "inheritedAuthenticationRestrictions"_sd; inline Status _badValue(const char* reason, int location) { return Status(ErrorCodes::BadValue, reason, location); @@ -453,25 +455,53 @@ Status V2UserDocumentParser::parseRoleVector(const BSONArray& rolesArray, Status V2UserDocumentParser::initializeAuthenticationRestrictionsFromUserDocument( const BSONObj& privDoc, User* user) const { + RestrictionDocuments::sequence_type restrictionVector; + + // Restrictions on the user const auto authenticationRestrictions = privDoc[AUTHENTICATION_RESTRICTIONS_FIELD_NAME]; - if (authenticationRestrictions.eoo()) { - return Status::OK(); - } + if (!authenticationRestrictions.eoo()) { + if (authenticationRestrictions.type() != Array) { + return Status(ErrorCodes::UnsupportedFormat, + "'authenticationRestrictions' field must be an array"); + } - if (authenticationRestrictions.type() != Array) { - return Status(ErrorCodes::UnsupportedFormat, - "'authenticationRestrictions' field must be an array"); + auto restrictions = + parseAuthenticationRestriction(BSONArray(authenticationRestrictions.Obj())); + if (!restrictions.isOK()) { + return restrictions.getStatus(); + } + + restrictionVector.push_back(restrictions.getValue()); } - auto restrictions = parseAuthenticationRestriction(BSONArray(authenticationRestrictions.Obj())); - if (!restrictions.isOK()) { - return restrictions.getStatus(); + // Restrictions from roles + const auto inherited = privDoc[INHERITED_AUTHENTICATION_RESTRICTIONS_FIELD_NAME]; + if (!inherited.eoo()) { + if (inherited.type() != Array) { + return Status(ErrorCodes::UnsupportedFormat, + "'inheritedAuthenticationRestrictions' field must be an array"); + } + + for (const auto& roleRestriction : BSONArray(inherited.Obj())) { + if (roleRestriction.type() != Array) { + return Status(ErrorCodes::UnsupportedFormat, + "'inheritedAuthenticationRestrictions' sub-fields must be arrays"); + } + + auto roleRestrictionDoc = + parseAuthenticationRestriction(BSONArray(roleRestriction.Obj())); + if (!roleRestrictionDoc.isOK()) { + return roleRestrictionDoc.getStatus(); + } + + restrictionVector.push_back(roleRestrictionDoc.getValue()); + } } - if (user != nullptr) { - user->setRestrictions( - RestrictionDocuments(RestrictionDocuments::sequence_type{restrictions.getValue()})); + if (user) { + user->setRestrictions(RestrictionDocuments(restrictionVector)); } + return Status::OK(); } diff --git a/src/mongo/db/auth/user_management_commands_parser.cpp b/src/mongo/db/auth/user_management_commands_parser.cpp index 6a170b59fc9..e712ad27cba 100644 --- a/src/mongo/db/auth/user_management_commands_parser.cpp +++ b/src/mongo/db/auth/user_management_commands_parser.cpp @@ -367,6 +367,7 @@ Status parseRolesInfoCommand(const BSONObj& cmdObj, StringData dbname, RolesInfo unordered_set<std::string> validFieldNames; validFieldNames.insert("rolesInfo"); validFieldNames.insert("showPrivileges"); + validFieldNames.insert("showAuthenticationRestrictions"); validFieldNames.insert("showBuiltinRoles"); Status status = _checkNoExtraFields(cmdObj, "rolesInfo", validFieldNames); @@ -411,6 +412,24 @@ Status parseRolesInfoCommand(const BSONObj& cmdObj, StringData dbname, RolesInfo << showPrivileges.toString()); } + const auto showAuthenticationRestrictions = cmdObj["showAuthenticationRestrictions"]; + if (showAuthenticationRestrictions.eoo()) { + parsedArgs->authenticationRestrictionsFormat = AuthenticationRestrictionsFormat::kOmit; + } else if (parsedArgs->privilegeFormat == PrivilegeFormat::kShowAsUserFragment) { + return Status( + ErrorCodes::UnsupportedFormat, + "showAuthenticationRestrictions may not be used with showPrivileges='asUserFragment'"); + } else { + bool show; + status = bsonExtractBooleanField(cmdObj, "showAuthenticationRestrictions", &show); + if (!status.isOK()) { + return status; + } + parsedArgs->authenticationRestrictionsFormat = show + ? AuthenticationRestrictionsFormat::kShow + : AuthenticationRestrictionsFormat::kOmit; + } + status = bsonExtractBooleanFieldWithDefault( cmdObj, "showBuiltinRoles", false, &parsedArgs->showBuiltinRoles); if (!status.isOK()) { diff --git a/src/mongo/db/auth/user_management_commands_parser.h b/src/mongo/db/auth/user_management_commands_parser.h index 8057a1061bb..5c942a97a09 100644 --- a/src/mongo/db/auth/user_management_commands_parser.h +++ b/src/mongo/db/auth/user_management_commands_parser.h @@ -34,6 +34,7 @@ #include "mongo/base/disallow_copying.h" #include "mongo/base/status.h" #include "mongo/base/string_data.h" +#include "mongo/db/auth/authorization_manager.h" #include "mongo/db/auth/privilege.h" #include "mongo/db/auth/privilege_format.h" #include "mongo/db/auth/role_name.h" @@ -114,11 +115,11 @@ Status parseUsersInfoCommand(const BSONObj& cmdObj, StringData dbname, UsersInfo struct RolesInfoArgs { std::vector<RoleName> roleNames; - bool allForDB; - PrivilegeFormat privilegeFormat; - bool showBuiltinRoles; - RolesInfoArgs() - : allForDB(false), privilegeFormat(PrivilegeFormat::kOmit), showBuiltinRoles(false) {} + bool allForDB = false; + PrivilegeFormat privilegeFormat = PrivilegeFormat::kOmit; + AuthenticationRestrictionsFormat authenticationRestrictionsFormat = + AuthenticationRestrictionsFormat::kOmit; + bool showBuiltinRoles = false; }; /** @@ -129,12 +130,10 @@ Status parseRolesInfoCommand(const BSONObj& cmdObj, StringData dbname, RolesInfo struct CreateOrUpdateRoleArgs { RoleName roleName; - bool hasRoles; + bool hasRoles = false; std::vector<RoleName> roles; - bool hasPrivileges; + bool hasPrivileges = false; PrivilegeVector privileges; - - CreateOrUpdateRoleArgs() : hasRoles(false), hasPrivileges(false) {} }; /** |