Status AuthzManagerExternalStateLocal::_getRoleDescription_inlock(const RoleName& roleName,
                                                                      bool showPrivileges,
                                                                      BSONObj* result) {
        if (!_roleGraph.roleExists(roleName))
            return Status(ErrorCodes::RoleNotFound, "No role named " + roleName.toString());

        mutablebson::Document resultDoc;
        fassert(17162, resultDoc.root().appendString(
                        AuthorizationManager::ROLE_NAME_FIELD_NAME, roleName.getRole()));
        fassert(17163, resultDoc.root().appendString(
                        AuthorizationManager::ROLE_SOURCE_FIELD_NAME, roleName.getDB()));
        fassert(17267,
                resultDoc.root().appendBool("isBuiltin", _roleGraph.isBuiltinRole(roleName)));
        mutablebson::Element rolesElement = resultDoc.makeElementArray("roles");
        fassert(17164, resultDoc.root().pushBack(rolesElement));
        mutablebson::Element inheritedRolesElement = resultDoc.makeElementArray("inheritedRoles");
        fassert(17165, resultDoc.root().pushBack(inheritedRolesElement));
        mutablebson::Element privilegesElement = resultDoc.makeElementArray("privileges");
        mutablebson::Element inheritedPrivilegesElement =
                resultDoc.makeElementArray("inheritedPrivileges");
        if (showPrivileges) {
            fassert(17166, resultDoc.root().pushBack(privilegesElement));
        }
        mutablebson::Element warningsElement = resultDoc.makeElementArray("warnings");

        addRoleNameObjectsToArrayElement(rolesElement, _roleGraph.getDirectSubordinates(roleName));
        if (_roleGraphState == roleGraphStateConsistent) {
            addRoleNameObjectsToArrayElement(
                    inheritedRolesElement, _roleGraph.getIndirectSubordinates(roleName));
            if (showPrivileges) {
                addPrivilegeObjectsOrWarningsToArrayElement(
                        privilegesElement,
                        warningsElement,
                        _roleGraph.getDirectPrivileges(roleName));

                addPrivilegeObjectsOrWarningsToArrayElement(
                        inheritedPrivilegesElement,
                        warningsElement,
                        _roleGraph.getAllPrivileges(roleName));

                fassert(17323, resultDoc.root().pushBack(inheritedPrivilegesElement));
            }
        }
        else if (showPrivileges) {
            warningsElement.appendString(
                    "", "Role graph state inconsistent; only direct privileges available.");
            addPrivilegeObjectsOrWarningsToArrayElement(
                    privilegesElement, warningsElement, _roleGraph.getDirectPrivileges(roleName));
        }
        if (warningsElement.hasChildren()) {
            fassert(17167, resultDoc.root().pushBack(warningsElement));
        }
        *result = resultDoc.getObject();
        return Status::OK();
    }
    Status AuthzManagerExternalStateMock::getUserDescription(
            const UserName& userName, BSONObj* result) {
        BSONObj privDoc;
        Status status = getPrivilegeDocument(userName, 2, &privDoc);
        if (!status.isOK())
            return status;

        unordered_set<RoleName> indirectRoles;
        PrivilegeVector allPrivileges;
        for (BSONObjIterator iter(privDoc["roles"].Obj()); iter.more(); iter.next()) {
            if (!(*iter)["hasRole"].trueValue())
                continue;
            RoleName roleName((*iter)[AuthorizationManager::ROLE_NAME_FIELD_NAME].str(),
                              (*iter)[AuthorizationManager::ROLE_SOURCE_FIELD_NAME].str());
            indirectRoles.insert(roleName);
            for (RoleNameIterator subordinates = _roleGraph.getIndirectSubordinates(
                         roleName);
                 subordinates.more();
                 subordinates.next()) {

                indirectRoles.insert(subordinates.get());
            }
            const PrivilegeVector& rolePrivileges(_roleGraph.getAllPrivileges(roleName));
            for (PrivilegeVector::const_iterator priv = rolePrivileges.begin(),
                     end = rolePrivileges.end();
                 priv != end;
                 ++priv) {

                Privilege::addPrivilegeToPrivilegeVector(&allPrivileges, *priv);
            }
        }

        mutablebson::Document userDoc(privDoc, mutablebson::Document::kInPlaceDisabled);
        mutablebson::Element indirectRolesElement = userDoc.makeElementArray("indirectRoles");
        mutablebson::Element privilegesElement = userDoc.makeElementArray("privileges");
        mutablebson::Element warningsElement = userDoc.makeElementArray("warnings");
        fassert(17180, userDoc.root().pushBack(privilegesElement));
        fassert(17181, userDoc.root().pushBack(indirectRolesElement));

        addRoleNameObjectsToArrayElement(indirectRolesElement,
                                         makeRoleNameIteratorForContainer(indirectRoles));
        addPrivilegeObjectsOrWarningsToArrayElement(
                privilegesElement, warningsElement, allPrivileges);
        if (warningsElement.hasChildren()) {
            fassert(17182, userDoc.root().pushBack(warningsElement));
        }
        *result = userDoc.getObject();
        return Status::OK();
    }
    Status AuthzManagerExternalStateLocal::getUserDescription(
            const UserName& userName,
            BSONObj* result) {

        BSONObj userDoc;
        Status status = _getUserDocument(userName, &userDoc);
        if (!status.isOK())
            return status;

        BSONElement directRolesElement;
        status = bsonExtractTypedField(userDoc, "roles", Array, &directRolesElement);
        if (!status.isOK())
            return status;
        std::vector<RoleName> directRoles;
        status = V2UserDocumentParser::parseRoleVector(BSONArray(directRolesElement.Obj()),
                                                       &directRoles);
        if (!status.isOK())
            return status;

        unordered_set<RoleName> indirectRoles;
        PrivilegeVector allPrivileges;
        bool isRoleGraphInconsistent;
        {
            boost::lock_guard<boost::mutex> lk(_roleGraphMutex);
            isRoleGraphInconsistent = _roleGraphState == roleGraphStateConsistent;
            for (size_t i = 0; i < directRoles.size(); ++i) {
                const RoleName& role(directRoles[i]);
                indirectRoles.insert(role);
                if (isRoleGraphInconsistent) {
                    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);
                }
            }
        }

        mutablebson::Document resultDoc(userDoc, mutablebson::Document::kInPlaceDisabled);
        mutablebson::Element inheritedRolesElement = resultDoc.makeElementArray("inheritedRoles");
        mutablebson::Element privilegesElement = resultDoc.makeElementArray("inheritedPrivileges");
        mutablebson::Element warningsElement = resultDoc.makeElementArray("warnings");
        fassert(17159, resultDoc.root().pushBack(inheritedRolesElement));
        fassert(17158, resultDoc.root().pushBack(privilegesElement));
        if (!isRoleGraphInconsistent) {
            fassert(17160, warningsElement.appendString(
                            "", "Role graph inconsistent, only direct privileges available."));
        }
        addRoleNameObjectsToArrayElement(inheritedRolesElement,
                                         makeRoleNameIteratorForContainer(indirectRoles));
        addPrivilegeObjectsOrWarningsToArrayElement(
                privilegesElement, warningsElement, allPrivileges);
        if (warningsElement.hasChildren()) {
            fassert(17161, resultDoc.root().pushBack(warningsElement));
        }
        *result = resultDoc.getObject();
        return Status::OK();
    }