Status AuthzManagerExternalStateMongos::getUserDescription(OperationContext* txn,
                                                           const UserName& userName,
                                                           BSONObj* result) {
    BSONObj usersInfoCmd =
        BSON("usersInfo" << BSON_ARRAY(BSON(AuthorizationManager::USER_NAME_FIELD_NAME
                                            << userName.getUser()
                                            << AuthorizationManager::USER_DB_FIELD_NAME
                                            << userName.getDB())) << "showPrivileges" << true
                         << "showCredentials" << true);
    BSONObjBuilder builder;
    const bool ok = grid.catalogManager()->runReadCommand("admin", usersInfoCmd, &builder);
    BSONObj cmdResult = builder.obj();
    if (!ok) {
        return Command::getStatusFromCommandResult(cmdResult);
    }

    std::vector<BSONElement> foundUsers = cmdResult["users"].Array();
    if (foundUsers.size() == 0) {
        return Status(ErrorCodes::UserNotFound, "User \"" + userName.toString() + "\" not found");
    }

    if (foundUsers.size() > 1) {
        return Status(ErrorCodes::UserDataInconsistent,
                      str::stream() << "Found multiple users on the \"" << userName.getDB()
                                    << "\" database with name \"" << userName.getUser() << "\"");
    }
    *result = foundUsers[0].Obj().getOwned();
    return Status::OK();
}
    Status AuthzManagerExternalState::getPrivilegeDocument(const UserName& userName,
                                                           int authzVersion,
                                                           BSONObj* result) {
        if (userName == internalSecurity.user->getName()) {
            return Status(ErrorCodes::InternalError,
                          "Requested privilege document for the internal user");
        }

        StringData dbname = userName.getDB();

        // Make sure the dbname is actually a database
        if (dbname == StringData("$external", StringData::LiteralTag()) ||
            dbname == AuthorizationManager::SERVER_RESOURCE_NAME ||
            dbname == AuthorizationManager::CLUSTER_RESOURCE_NAME) {
            return Status(ErrorCodes::UserNotFound,
                          mongoutils::str::stream() << "No privilege documents stored in the " <<
                          dbname << " user source.");
        }

        if (!NamespaceString::validDBName(dbname)) {
            return Status(ErrorCodes::BadValue,
                          mongoutils::str::stream() << "Bad database name \"" << dbname << "\"");
        }

        // Build the query needed to get the privilege document
        std::string usersNamespace;
        BSONObjBuilder queryBuilder;
        if (authzVersion == 1) {
            usersNamespace = mongoutils::str::stream() << dbname << ".system.users";
            queryBuilder.append(AuthorizationManager::V1_USER_NAME_FIELD_NAME, userName.getUser());
            queryBuilder.appendNull(AuthorizationManager::V1_USER_SOURCE_FIELD_NAME);
        } else if (authzVersion == 2) {
            usersNamespace = "admin.system.users";
            queryBuilder.append(AuthorizationManager::USER_NAME_FIELD_NAME, userName.getUser());
            queryBuilder.append(AuthorizationManager::USER_SOURCE_FIELD_NAME, userName.getDB());
        } else {
            return Status(ErrorCodes::UnsupportedFormat,
                          mongoutils::str::stream() <<
                                  "Unrecognized authorization format version: " << authzVersion);
        }

        // Query for the privilege document
        BSONObj userBSONObj;
        Status found = _findUser(usersNamespace, queryBuilder.obj(), &userBSONObj);
        if (!found.isOK()) {
            if (found.code() == ErrorCodes::UserNotFound) {
                // Return more detailed status that includes user name.
                return Status(ErrorCodes::UserNotFound,
                              mongoutils::str::stream() << "auth: couldn't find user " <<
                                      userName.toString() << ", " << usersNamespace,
                              0);
            } else {
                return found;
            }
        }

        *result = userBSONObj.getOwned();
        return Status::OK();
    }
    Status AuthzManagerExternalState::getPrivilegeDocument(const std::string& dbname,
                                                           const UserName& userName,
                                                           BSONObj* result) const {

        if (dbname == StringData("$external", StringData::LiteralTag()) ||
            dbname == AuthorizationManager::SERVER_RESOURCE_NAME ||
            dbname == AuthorizationManager::CLUSTER_RESOURCE_NAME) {
            return Status(ErrorCodes::UserNotFound,
                          mongoutils::str::stream() << "No privilege documents stored in the " <<
                          dbname << " user source.");
        }

        if (!NamespaceString::validDBName(dbname)) {
            return Status(ErrorCodes::BadValue, "Bad database name \"" + dbname + "\"");
        }

        if (userName == internalSecurity.user) {
            if (internalSecurity.pwd.empty()) {
                return Status(ErrorCodes::UserNotFound,
                              "key file must be used to log in with internal user",
                              15889);
            }
            *result = BSON(AuthorizationManager::USER_NAME_FIELD_NAME <<
                           internalSecurity.user.getUser() <<
                           AuthorizationManager::PASSWORD_FIELD_NAME <<
                           internalSecurity.pwd).getOwned();
            return Status::OK();
        }

        std::string usersNamespace = dbname + ".system.users";

        BSONObj userBSONObj;
        BSONObjBuilder queryBuilder;
        queryBuilder.append(AuthorizationManager::USER_NAME_FIELD_NAME, userName.getUser());
        if (userName.getDB() == dbname) {
            queryBuilder.appendNull(AuthorizationManager::USER_SOURCE_FIELD_NAME);
        }
        else {
            queryBuilder.append(AuthorizationManager::USER_SOURCE_FIELD_NAME,
                                userName.getDB());
        }

        Status found = _findUser(usersNamespace, queryBuilder.obj(), &userBSONObj);
        if (!found.isOK()) {
            if (found.code() == ErrorCodes::UserNotFound) {
                // Return more detailed status that includes user name.
                return Status(ErrorCodes::UserNotFound,
                              mongoutils::str::stream() << "auth: couldn't find user " <<
                                      userName.toString() << ", " << usersNamespace,
                              0);
            } else {
                return found;
            }
        }

        *result = userBSONObj.getOwned();
        return Status::OK();
    }
void SASLServerMechanismRegistry::advertiseMechanismNamesForUser(OperationContext* opCtx,
                                                                 const BSONObj& isMasterCmd,
                                                                 BSONObjBuilder* builder) {
    BSONElement saslSupportedMechs = isMasterCmd["saslSupportedMechs"];
    if (saslSupportedMechs.type() == BSONType::String) {

        UserName userName = uassertStatusOK(UserName::parse(saslSupportedMechs.String()));


        // Authenticating the __system@local user to the admin database on mongos is required
        // by the auth passthrough test suite.
        if (getTestCommandsEnabled() &&
            userName.getUser() == internalSecurity.user->getName().getUser() &&
            userName.getDB() == "admin") {
            userName = internalSecurity.user->getName();
        }

        AuthorizationManager* authManager = AuthorizationManager::get(opCtx->getServiceContext());

        UserHandle user;
        const auto swUser = authManager->acquireUser(opCtx, userName);
        if (!swUser.isOK()) {
            auto& status = swUser.getStatus();
            if (status.code() == ErrorCodes::UserNotFound) {
                log() << "Supported SASL mechanisms requested for unknown user '" << userName
                      << "'";
                return;
            }
            uassertStatusOK(status);
        }

        user = std::move(swUser.getValue());
        BSONArrayBuilder mechanismsBuilder;
        const auto& mechList = _getMapRef(userName.getDB());

        for (const auto& factoryIt : mechList) {
            SecurityPropertySet properties = factoryIt->properties();
            if (!properties.hasAllProperties(SecurityPropertySet{SecurityProperty::kNoPlainText,
                                                                 SecurityProperty::kMutualAuth}) &&
                userName.getDB() != "$external") {
                continue;
            }

            auto mechanismEnabled = _mechanismSupportedByConfig(factoryIt->mechanismName());
            if (!mechanismEnabled && userName == internalSecurity.user->getName()) {
                mechanismEnabled = factoryIt->isInternalAuthMech();
            }

            if (mechanismEnabled && factoryIt->canMakeMechanismForUser(user.get())) {
                mechanismsBuilder << factoryIt->mechanismName();
            }
        }

        builder->appendArray("saslSupportedMechs", mechanismsBuilder.arr());
    }
}
    Status AuthzManagerExternalState::getPrivilegeDocumentV1(const StringData& dbname,
                                                             const UserName& userName,
                                                             BSONObj* result) {
        if (userName == internalSecurity.user->getName()) {
            return Status(ErrorCodes::InternalError,
                          "Requested privilege document for the internal user");
        }

        if (!NamespaceString::validDBName(dbname)) {
            return Status(ErrorCodes::BadValue,
                          mongoutils::str::stream() << "Bad database name \"" << dbname << "\"");
        }

        const bool isUserFromTargetDB = (dbname == userName.getDB());

        // Build the query needed to get the privilege document

        BSONObjBuilder queryBuilder;
        const NamespaceString usersNamespace(dbname, "system.users");
        queryBuilder.append(AuthorizationManager::V1_USER_NAME_FIELD_NAME, userName.getUser());
        if (isUserFromTargetDB) {
            queryBuilder.appendNull(AuthorizationManager::V1_USER_SOURCE_FIELD_NAME);
        }
        else {
            queryBuilder.append(AuthorizationManager::V1_USER_SOURCE_FIELD_NAME, userName.getDB());
        }

        // Query for the privilege document
        BSONObj userBSONObj;
        Status found = findOne(usersNamespace, queryBuilder.done(), &userBSONObj);
        if (!found.isOK()) {
            if (found.code() == ErrorCodes::NoMatchingDocument) {
                // Return more detailed status that includes user name.
                return Status(ErrorCodes::UserNotFound,
                              mongoutils::str::stream() << "auth: couldn't find user " <<
                              userName.toString() << ", " << usersNamespace.ns(),
                              0);
            } else {
                return found;
            }
        }

        if (isUserFromTargetDB) {
            if (userBSONObj[AuthorizationManager::PASSWORD_FIELD_NAME].eoo()) {
                return Status(ErrorCodes::AuthSchemaIncompatible, mongoutils::str::stream() <<
                              "User documents with schema version " <<
                              AuthorizationManager::schemaVersion24 <<
                              " must have a \"" <<
                              AuthorizationManager::PASSWORD_FIELD_NAME <<
                              "\" field.");
            }
        }

        *result = userBSONObj.getOwned();
        return Status::OK();
    }
bool CmdAuthenticate::run(OperationContext* opCtx,
                          const string& dbname,
                          const BSONObj& cmdObj,
                          BSONObjBuilder& result) {
    if (!serverGlobalParams.quiet.load()) {
        mutablebson::Document cmdToLog(cmdObj, mutablebson::Document::kInPlaceDisabled);
        redactForLogging(&cmdToLog);
        log() << " authenticate db: " << dbname << " " << cmdToLog;
    }
    std::string mechanism = cmdObj.getStringField("mechanism");
    if (mechanism.empty()) {
        mechanism = "MONGODB-CR";
    }
    UserName user;
    auto& sslPeerInfo = SSLPeerInfo::forSession(opCtx->getClient()->session());
    if (mechanism == "MONGODB-X509" && !cmdObj.hasField("user")) {
        user = UserName(sslPeerInfo.subjectName, dbname);
    } else {
        user = UserName(cmdObj.getStringField("user"), dbname);
    }

    if (Command::testCommandsEnabled && user.getDB() == "admin" &&
        user.getUser() == internalSecurity.user->getName().getUser()) {
        // Allows authenticating as the internal user against the admin database.  This is to
        // support the auth passthrough test framework on mongos (since you can't use the local
        // database on a mongos, so you can't auth as the internal user without this).
        user = internalSecurity.user->getName();
    }

    Status status = _authenticate(opCtx, mechanism, user, cmdObj);
    audit::logAuthentication(Client::getCurrent(), mechanism, user, status.code());
    if (!status.isOK()) {
        if (!serverGlobalParams.quiet.load()) {
            auto const client = opCtx->getClient();
            log() << "Failed to authenticate " << user
                  << (client->hasRemote() ? (" from client " + client->getRemote().toString()) : "")
                  << " with mechanism " << mechanism << ": " << status;
        }
        if (status.code() == ErrorCodes::AuthenticationFailed) {
            // Statuses with code AuthenticationFailed may contain messages we do not wish to
            // reveal to the user, so we return a status with the message "auth failed".
            appendCommandStatus(result, Status(ErrorCodes::AuthenticationFailed, "auth failed"));
        } else {
            appendCommandStatus(result, status);
        }
        sleepmillis(saslGlobalParams.authFailedDelay.load());
        return false;
    }
    result.append("dbname", user.getDB());
    result.append("user", user.getUser());
    return true;
}
    Status AuthzManagerExternalStateMongod::updatePrivilegeDocument(
            const UserName& user, const BSONObj& updateObj) const {
        string userNS = mongoutils::str::stream() << user.getDB() << ".system.users";
        Client::GodScope gs;
        Client::WriteContext ctx(userNS);

        DBDirectClient client;
        client.update(userNS,
                      QUERY("user" << user.getUser() << "userSource" << BSONNULL),
                      updateObj);

        // 30 second timeout for w:majority
        BSONObj res = client.getLastErrorDetailed(false, false, -1, 30*1000);
        string err = client.getLastErrorString(res);
        if (!err.empty()) {
            return Status(ErrorCodes::UserModificationFailed, err);
        }

        int numUpdated = res["n"].numberInt();
        dassert(numUpdated <= 1 && numUpdated >= 0);
        if (numUpdated == 0) {
            return Status(ErrorCodes::UserNotFound,
                          mongoutils::str::stream() << "User " << user.getFullName() <<
                                  " not found");
        }

        return Status::OK();
    }
 Status AuthzManagerExternalStateMongos::getUserDescription(const UserName& userName,
                                                            BSONObj* result) {
     try {
         scoped_ptr<ScopedDbConnection> conn(getConnectionForAuthzCollection(
                 AuthorizationManager::usersCollectionNamespace));
         BSONObj cmdResult;
         conn->get()->runCommand(
                 "admin",
                 BSON("usersInfo" <<
                      BSON_ARRAY(BSON(AuthorizationManager::USER_NAME_FIELD_NAME <<
                                      userName.getUser() <<
                                      AuthorizationManager::USER_SOURCE_FIELD_NAME <<
                                      userName.getDB())) <<
                      "showPrivileges" << true <<
                      "showCredentials" << true),
                 cmdResult);
         if (!cmdResult["ok"].trueValue()) {
             int code = cmdResult["code"].numberInt();
             if (code == 0) code = ErrorCodes::UnknownError;
             return Status(ErrorCodes::Error(code), cmdResult["errmsg"].str());
         }
         *result = cmdResult["users"]["0"].Obj().getOwned();
         conn->done();
         return Status::OK();
     } catch (const DBException& e) {
         return e.toStatus();
     }
 }
    Status AuthzManagerExternalStateMongod::_getUserDocument(const UserName& userName,
                                                             BSONObj* userDoc) {

        Client::ReadContext ctx("admin");
        int authzVersion;
        Status status = getStoredAuthorizationVersion(&authzVersion);
        if (!status.isOK())
            return status;

        switch (authzVersion) {
        case AuthorizationManager::schemaVersion26Upgrade:
        case AuthorizationManager::schemaVersion26Final:
            break;
        default:
            return Status(ErrorCodes::AuthSchemaIncompatible, mongoutils::str::stream() <<
                          "Unsupported schema version for getUserDescription(): " <<
                          authzVersion);
        }

        status = findOne(
                (authzVersion == AuthorizationManager::schemaVersion26Final ?
                 AuthorizationManager::usersCollectionNamespace :
                 AuthorizationManager::usersAltCollectionNamespace),
                BSON(AuthorizationManager::USER_NAME_FIELD_NAME << userName.getUser() <<
                     AuthorizationManager::USER_DB_FIELD_NAME << userName.getDB()),
                userDoc);
        if (status == ErrorCodes::NoMatchingDocument) {
            status = Status(ErrorCodes::UserNotFound, mongoutils::str::stream() <<
                            "Could not find user " << userName.getFullName());
        }
        return status;
    }
    Status AuthzManagerExternalStateMongos::updatePrivilegeDocument(
            const UserName& user, const BSONObj& updateObj) {
        try {
            string userNS = mongoutils::str::stream() << user.getDB() << ".system.users";
            scoped_ptr<ScopedDbConnection> conn(getConnectionForUsersCollection(userNS));

            conn->get()->update(userNS,
                                QUERY("user" << user.getUser() << "userSource" << BSONNULL),
                                updateObj);

            // 30 second timeout for w:majority
            BSONObj res = conn->get()->getLastErrorDetailed(false, false, -1, 30*1000);
            string err = conn->get()->getLastErrorString(res);
            conn->done();

            if (!err.empty()) {
                return Status(ErrorCodes::UserModificationFailed, err);
            }

            int numUpdated = res["n"].numberInt();
            dassert(numUpdated <= 1 && numUpdated >= 0);
            if (numUpdated == 0) {
                return Status(ErrorCodes::UserNotFound,
                              mongoutils::str::stream() << "User " << user.getFullName() <<
                                      " not found");
            }

            return Status::OK();
        } catch (const DBException& e) {
            return e.toStatus();
        }
    }
Status checkAuthForDropUserCommand(Client* client,
                                   const std::string& dbname,
                                   const BSONObj& cmdObj) {
    AuthorizationSession* authzSession = AuthorizationSession::get(client);
    UserName userName;
    Status status = auth::parseAndValidateDropUserCommand(cmdObj, dbname, &userName);
    if (!status.isOK()) {
        return status;
    }

    if (!authzSession->isAuthorizedForActionsOnResource(
            ResourcePattern::forDatabaseName(userName.getDB()), ActionType::dropUser)) {
        return Status(ErrorCodes::Unauthorized,
                      str::stream() << "Not authorized to drop users from the " << userName.getDB()
                                    << " database");
    }
    return Status::OK();
}
    Status AuthzManagerExternalState::getPrivilegeDocumentV1(const StringData& dbname,
                                                             const UserName& userName,
                                                             BSONObj* result) {
        if (userName == internalSecurity.user->getName()) {
            return Status(ErrorCodes::InternalError,
                          "Requested privilege document for the internal user");
        }

        if (!NamespaceString::validDBName(dbname)) {
            return Status(ErrorCodes::BadValue,
                          mongoutils::str::stream() << "Bad database name \"" << dbname << "\"");
        }

        // Build the query needed to get the privilege document
        std::string usersNamespace;
        BSONObjBuilder queryBuilder;
        usersNamespace = mongoutils::str::stream() << dbname << ".system.users";
        queryBuilder.append(AuthorizationManager::V1_USER_NAME_FIELD_NAME, userName.getUser());
        if (dbname == userName.getDB()) {
            queryBuilder.appendNull(AuthorizationManager::V1_USER_SOURCE_FIELD_NAME);
        }
        else {
            queryBuilder.append(AuthorizationManager::V1_USER_SOURCE_FIELD_NAME, userName.getDB());
        }

        // Query for the privilege document
        BSONObj userBSONObj;
        Status found = _findUser(usersNamespace, queryBuilder.done(), &userBSONObj);
        if (!found.isOK()) {
            if (found.code() == ErrorCodes::UserNotFound) {
                // Return more detailed status that includes user name.
                return Status(ErrorCodes::UserNotFound,
                              mongoutils::str::stream() << "auth: couldn't find user " <<
                                      userName.toString() << ", " << usersNamespace,
                              0);
            } else {
                return found;
            }
        }

        *result = userBSONObj.getOwned();
        return Status::OK();
    }
    Status CmdAuthenticate::_authenticateX509(const UserName& user, const BSONObj& cmdObj) {
        if (!getSSLManager()) {
            return Status(ErrorCodes::ProtocolError,
                          "SSL support is required for the MONGODB-X509 mechanism.");
        }
        if(user.getDB() != "$external") {
            return Status(ErrorCodes::ProtocolError,
                          "X.509 authentication must always use the $external database.");
        }

        ClientBasic *client = ClientBasic::getCurrent();
        AuthorizationSession* authorizationSession = client->getAuthorizationSession();
        std::string subjectName = client->port()->getX509SubjectName();

        if (user.getUser() != subjectName) {
            return Status(ErrorCodes::AuthenticationFailed,
                          "There is no x.509 client certificate matching the user.");
        }
        else {
            std::string srvSubjectName = getSSLManager()->getServerSubjectName();
            
            size_t srvClusterIdPos = srvSubjectName.find(",OU=");
            size_t peerClusterIdPos = subjectName.find(",OU=");

            std::string srvClusterId = srvClusterIdPos != std::string::npos ? 
                srvSubjectName.substr(srvClusterIdPos) : "";
            std::string peerClusterId = peerClusterIdPos != std::string::npos ? 
                subjectName.substr(peerClusterIdPos) : "";

            // Handle internal cluster member auth, only applies to server-server connections
            int clusterAuthMode = serverGlobalParams.clusterAuthMode.load(); 
            if (srvClusterId == peerClusterId && !srvClusterId.empty()) {
                if (clusterAuthMode == ServerGlobalParams::ClusterAuthMode_undefined ||
                    clusterAuthMode == ServerGlobalParams::ClusterAuthMode_keyFile) {
                    return Status(ErrorCodes::AuthenticationFailed, "The provided certificate " 
                                  "can only be used for cluster authentication, not client " 
                                  "authentication. The current configuration does not allow " 
                                  "x.509 cluster authentication, check the --clusterAuthMode flag");
                }
                authorizationSession->grantInternalAuthorization();
            }
            // Handle normal client authentication, only applies to client-server connections
            else {
                if (_isX509AuthDisabled) {
                    return Status(ErrorCodes::BadValue,
                                  _x509AuthenticationDisabledMessage);
                }
                Status status = authorizationSession->addAndAuthorizeUser(user);
                if (!status.isOK()) {
                    return status;
                }
            }
            return Status::OK();
        }
    }
    Status AuthzManagerExternalStateMongos::getUserDescription(
                    OperationContext* txn, const UserName& userName, BSONObj* result) {
        try {
            scoped_ptr<ScopedDbConnection> conn(getConnectionForAuthzCollection(
                    AuthorizationManager::usersCollectionNamespace));
            BSONObj cmdResult;
            conn->get()->runCommand(
                    "admin",
                    BSON("usersInfo" <<
                         BSON_ARRAY(BSON(AuthorizationManager::USER_NAME_FIELD_NAME <<
                                         userName.getUser() <<
                                         AuthorizationManager::USER_DB_FIELD_NAME <<
                                         userName.getDB())) <<
                         "showPrivileges" << true <<
                         "showCredentials" << true),
                    cmdResult);
            if (!cmdResult["ok"].trueValue()) {
                int code = cmdResult["code"].numberInt();
                if (code == 0) code = ErrorCodes::UnknownError;
                return Status(ErrorCodes::Error(code), cmdResult["errmsg"].str());
            }

            std::vector<BSONElement> foundUsers = cmdResult["users"].Array();
            if (foundUsers.size() == 0) {
                return Status(ErrorCodes::UserNotFound,
                              "User \"" + userName.toString() + "\" not found");
            }
            if (foundUsers.size() > 1) {
                return Status(ErrorCodes::UserDataInconsistent,
                              mongoutils::str::stream() << "Found multiple users on the \"" <<
                                      userName.getDB() << "\" database with name \"" <<
                                      userName.getUser() << "\"");
            }
            *result = foundUsers[0].Obj().getOwned();
            conn->done();
            return Status::OK();
        } catch (const DBException& e) {
            return e.toStatus();
        }
    }
    Status CmdAuthenticate::_authenticateX509(
                    OperationContext* txn, const UserName& user, const BSONObj& cmdObj) {
        if (!getSSLManager()) {
            return Status(ErrorCodes::ProtocolError,
                          "SSL support is required for the MONGODB-X509 mechanism.");
        }
        if(user.getDB() != "$external") {
            return Status(ErrorCodes::ProtocolError,
                          "X.509 authentication must always use the $external database.");
        }

        ClientBasic *client = ClientBasic::getCurrent();
        AuthorizationSession* authorizationSession = AuthorizationSession::get(client);
        std::string subjectName = client->port()->getX509SubjectName();

        if (!getSSLManager()->getSSLConfiguration().hasCA) {
            return Status(ErrorCodes::AuthenticationFailed,
                          "Unable to verify x.509 certificate, as no CA has been provided.");
        }
        else if (user.getUser() != subjectName) {
            return Status(ErrorCodes::AuthenticationFailed,
                          "There is no x.509 client certificate matching the user.");
        }
        else {
            std::string srvSubjectName = getSSLManager()->getSSLConfiguration().serverSubjectName;
 
            // Handle internal cluster member auth, only applies to server-server connections
            if (_clusterIdMatch(subjectName, srvSubjectName)) {
                int clusterAuthMode = serverGlobalParams.clusterAuthMode.load(); 
                if (clusterAuthMode == ServerGlobalParams::ClusterAuthMode_undefined ||
                    clusterAuthMode == ServerGlobalParams::ClusterAuthMode_keyFile) {
                    return Status(ErrorCodes::AuthenticationFailed, "The provided certificate " 
                                  "can only be used for cluster authentication, not client " 
                                  "authentication. The current configuration does not allow " 
                                  "x.509 cluster authentication, check the --clusterAuthMode flag");
                }
                authorizationSession->grantInternalAuthorization();
            }
            // Handle normal client authentication, only applies to client-server connections
            else {
                if (_isX509AuthDisabled) {
                    return Status(ErrorCodes::BadValue,
                                  _x509AuthenticationDisabledMessage);
                }
                Status status = authorizationSession->addAndAuthorizeUser(txn, user);
                if (!status.isOK()) {
                    return status;
                }
            }
            return Status::OK();
        }
    }
 Status AuthorizationSession::acquirePrivilege(const Privilege& privilege,
                                               const UserName& authorizingUser) {
     if (!_authenticatedPrincipals.lookup(authorizingUser)) {
         return Status(ErrorCodes::UserNotFound,
                       mongoutils::str::stream()
                               << "No authenticated user found with name: "
                               << authorizingUser.getUser()
                               << " from database "
                               << authorizingUser.getDB(),
                       0);
     }
     _acquiredPrivileges.grantPrivilege(privilege, authorizingUser);
     return Status::OK();
 }
        bool run(const string& dbname,
                 BSONObj& cmdObj,
                 int options,
                 string& errmsg,
                 BSONObjBuilder& result,
                 bool fromRepl) {
            AuthorizationManager* authzManager = getGlobalAuthorizationManager();
            AuthzDocumentsUpdateGuard updateGuard(authzManager);
            if (!updateGuard.tryLock("Remove user")) {
                addStatus(Status(ErrorCodes::LockBusy, "Could not lock auth data update lock."),
                          result);
                return false;
            }

            UserName userName;
            BSONObj writeConcern;

            Status status = auth::parseAndValidateRemoveUserCommand(cmdObj,
                                                                    dbname,
                                                                    &userName,
                                                                    &writeConcern);
            if (!status.isOK()) {
                addStatus(status, result);
                return false;
            }

            int numUpdated;
            status = authzManager->removePrivilegeDocuments(
                    BSON(AuthorizationManager::USER_NAME_FIELD_NAME << userName.getUser() <<
                         AuthorizationManager::USER_SOURCE_FIELD_NAME << userName.getDB()),
                    writeConcern,
                    &numUpdated);
            // Must invalidate even on bad status - what if the write succeeded but the GLE failed?
            authzManager->invalidateUserByName(userName);
            if (!status.isOK()) {
                addStatus(status, result);
                return false;
            }

            if (numUpdated == 0) {
                addStatus(Status(ErrorCodes::UserNotFound,
                                 mongoutils::str::stream() << "User '" << userName.getFullName() <<
                                         "' not found"),
                          result);
                return false;
            }

            return true;
        }
 Status AuthzManagerExternalStateLocal::_getUserDocument(OperationContext* txn,
                                                         const UserName& userName,
                                                         BSONObj* userDoc) {
     Status status = findOne(
             txn,
             AuthorizationManager::usersCollectionNamespace,
             BSON(AuthorizationManager::USER_NAME_FIELD_NAME << userName.getUser() <<
                  AuthorizationManager::USER_DB_FIELD_NAME << userName.getDB()),
             userDoc);
     if (status == ErrorCodes::NoMatchingDocument) {
         status = Status(ErrorCodes::UserNotFound, mongoutils::str::stream() <<
                         "Could not find user " << userName.getFullName());
     }
     return status;
 }
 Status AuthorizationSession::acquirePrivilegesFromPrivilegeDocument(
         const std::string& dbname, const UserName& user, const BSONObj& privilegeDocument) {
     if (!_authenticatedPrincipals.lookup(user)) {
         return Status(ErrorCodes::UserNotFound,
                       mongoutils::str::stream()
                               << "No authenticated principle found with name: "
                               << user.getUser()
                               << " from database "
                               << user.getDB(),
                       0);
     }
     return _externalState->getAuthorizationManager().buildPrivilegeSet(dbname,
                                                                        user,
                                                                        privilegeDocument,
                                                                        &_acquiredPrivileges);
 }
bool AuthorizationSession::isAuthorizedToChangeAsUser(const UserName& userName,
                                                      ActionType actionType) {
    User* user = lookupUser(userName);
    if (!user) {
        return false;
    }
    ResourcePattern resourceSearchList[resourceSearchListCapacity];
    const int resourceSearchListLength = buildResourceSearchList(
        ResourcePattern::forDatabaseName(userName.getDB()), resourceSearchList);

    ActionSet actions;
    for (int i = 0; i < resourceSearchListLength; ++i) {
        actions.addAllActionsFromSet(user->getActionsForResource(resourceSearchList[i]));
    }
    return actions.contains(actionType);
}
    Status AuthzManagerExternalStateMongod::updatePrivilegeDocument(
            const UserName& user, const BSONObj& updateObj, const BSONObj& writeConcern) {
        try {
            const std::string userNS = "admin.system.users";
            DBDirectClient client;
            {
                Client::GodScope gs;
                // TODO(spencer): Once we're no longer fully rebuilding the user cache on every
                // change to user data we should remove the global lock and uncomment the
                // WriteContext below
                Lock::GlobalWrite w;
                // Client::WriteContext ctx(userNS);
                client.update(userNS,
                              QUERY(AuthorizationManager::USER_NAME_FIELD_NAME << user.getUser() <<
                                    AuthorizationManager::USER_SOURCE_FIELD_NAME << user.getDB()),
                              updateObj);
            }

            // Handle write concern
            BSONObjBuilder gleBuilder;
            gleBuilder.append("getLastError", 1);
            gleBuilder.appendElements(writeConcern);
            BSONObj res;
            client.runCommand("admin", gleBuilder.done(), res);
            string err = client.getLastErrorString(res);
            if (!err.empty()) {
                return Status(ErrorCodes::UserModificationFailed, err);
            }

            int numUpdated = res["n"].numberInt();
            dassert(numUpdated <= 1 && numUpdated >= 0);
            if (numUpdated == 0) {
                return Status(ErrorCodes::UserNotFound,
                              mongoutils::str::stream() << "User " << user.getFullName() <<
                                      " not found");
            }

            return Status::OK();
        } catch (const DBException& e) {
            return e.toStatus();
        }
    }
 Status AuthzManagerExternalStateMongos::getUserDescription(const UserName& userName,
                                                            BSONObj* result) {
     try {
         scoped_ptr<ScopedDbConnection> conn(getConnectionForAuthzCollection(
                 AuthorizationManager::usersCollectionNamespace));
         BSONObj cmdResult;
         conn->get()->runCommand(
                 userName.getDB().toString(),  // TODO: Change usersInfo so this command can always go to "admin".
                 BSON("usersInfo" << userName.getUser() << "details" << true),
                 cmdResult);
         if (!cmdResult["ok"].trueValue()) {
             int code = cmdResult["code"].numberInt();
             if (code == 0) code = ErrorCodes::UnknownError;
             return Status(ErrorCodes::Error(code), cmdResult["errmsg"].str());
         }
         *result = cmdResult["users"]["0"].Obj().getOwned();
         conn->done();
         return Status::OK();
     } catch (const DBException& e) {
         return e.toStatus();
     }
 }
 Status AuthzManagerExternalState::updatePrivilegeDocument(
         const UserName& user, const BSONObj& updateObj, const BSONObj& writeConcern) {
     Status status = updateOne(
             NamespaceString("admin.system.users"),
             BSON(AuthorizationManager::USER_NAME_FIELD_NAME << user.getUser() <<
                  AuthorizationManager::USER_DB_FIELD_NAME << user.getDB()),
             updateObj,
             false,
             writeConcern);
     if (status.isOK()) {
         return status;
     }
     if (status.code() == ErrorCodes::NoMatchingDocument) {
         return Status(ErrorCodes::UserNotFound,
                       mongoutils::str::stream() << "User " << user.getFullName() <<
                               " not found");
     }
     if (status.code() == ErrorCodes::UnknownError) {
         return Status(ErrorCodes::UserModificationFailed, status.reason());
     }
     return status;
 }
    Status AuthzManagerExternalStateMongos::updatePrivilegeDocument(
            const UserName& user, const BSONObj& updateObj, const BSONObj& writeConcern) {
        try {
            const std::string userNS = "admin.system.users";
            scoped_ptr<ScopedDbConnection> conn(getConnectionForAuthzCollection(userNS));

            conn->get()->update(
                    userNS,
                    QUERY(AuthorizationManager::USER_NAME_FIELD_NAME << user.getUser() <<
                          AuthorizationManager::USER_SOURCE_FIELD_NAME << user.getDB()),
                    updateObj);

            // Handle write concern
            BSONObjBuilder gleBuilder;
            gleBuilder.append("getLastError", 1);
            gleBuilder.appendElements(writeConcern);
            BSONObj res;
            conn->get()->runCommand("admin", gleBuilder.done(), res);
            string err = conn->get()->getLastErrorString(res);
            conn->done();

            if (!err.empty()) {
                return Status(ErrorCodes::UserModificationFailed, err);
            }

            int numUpdated = res["n"].numberInt();
            dassert(numUpdated <= 1 && numUpdated >= 0);
            if (numUpdated == 0) {
                return Status(ErrorCodes::UserNotFound,
                              mongoutils::str::stream() << "User " << user.getFullName() <<
                                      " not found");
            }

            return Status::OK();
        } catch (const DBException& e) {
            return e.toStatus();
        }
    }
    Status AuthzManagerExternalStateMongod::updatePrivilegeDocument(
            const UserName& user, const BSONObj& updateObj) {
        try {
            string userNS = mongoutils::str::stream() << user.getDB() << ".system.users";
            DBDirectClient client;
            {
                Client::GodScope gs;
                // TODO(spencer): Once we're no longer fully rebuilding the user cache on every
                // change to user data we should remove the global lock and uncomment the
                // WriteContext below
                Lock::GlobalWrite w;
                // Client::WriteContext ctx(userNS);
                client.update(userNS,
                              QUERY("user" << user.getUser() << "userSource" << BSONNULL),
                              updateObj);
            }

            // 30 second timeout for w:majority
            BSONObj res = client.getLastErrorDetailed(false, false, -1, 30*1000);
            string err = client.getLastErrorString(res);
            if (!err.empty()) {
                return Status(ErrorCodes::UserModificationFailed, err);
            }

            int numUpdated = res["n"].numberInt();
            dassert(numUpdated <= 1 && numUpdated >= 0);
            if (numUpdated == 0) {
                return Status(ErrorCodes::UserNotFound,
                              mongoutils::str::stream() << "User " << user.getFullName() <<
                                      " not found");
            }

            return Status::OK();
        } catch (const DBException& e) {
            return e.toStatus();
        }
    }
    Status CmdAuthenticate::_authenticateX509(const UserName& user, const BSONObj& cmdObj) {
        if(user.getDB() != "$external") {
            return Status(ErrorCodes::ProtocolError,
                          "X.509 authentication must always use the $external database.");
        }

        ClientBasic *client = ClientBasic::getCurrent();
        AuthorizationSession* authorizationSession = client->getAuthorizationSession();
        StringData subjectName = client->port()->getX509SubjectName();

        if (user.getUser() != subjectName) {
            return Status(ErrorCodes::AuthenticationFailed,
                          "There is no x.509 client certificate matching the user.");
        }
        else {
            StringData srvSubjectName = getSSLManager()->getServerSubjectName();
            StringData srvClusterId = srvSubjectName.substr(srvSubjectName.find(",OU="));
            StringData peerClusterId = subjectName.substr(subjectName.find(",OU="));

            fassert(17002, !srvClusterId.empty() && srvClusterId != srvSubjectName);

            // Handle internal cluster member auth, only applies to server-server connections 
            if (srvClusterId == peerClusterId) {
                if (cmdLine.clusterAuthMode.empty() || cmdLine.clusterAuthMode == "keyfile") {
                    return Status(ErrorCodes::AuthenticationFailed,
                                  "X509 authentication is not allowed for cluster authentication");
                }
                authorizationSession->grantInternalAuthorization(user);
            }
            // Handle normal client authentication, only applies to client-server connections
            else {
                Principal* principal = new Principal(user);
                authorizationSession->addAndAuthorizePrincipal(principal);
            }
            return Status::OK();
        }
    }
    Status AuthzManagerExternalStateMongod::getUserDescription(const UserName& userName,
                                                               BSONObj* result) {

        BSONObj userDoc;
        Status status = _findUser(
                "admin.system.users",
                BSON(AuthorizationManager::USER_NAME_FIELD_NAME << userName.getUser() <<
                     AuthorizationManager::USER_SOURCE_FIELD_NAME << userName.getDB()),
                &userDoc);
        if (!status.isOK())
            return status;

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

        unordered_set<RoleName> indirectRoles;
        PrivilegeVector allPrivileges;
        {
            boost::lock_guard<boost::mutex> lk(_roleGraphMutex);

            for (size_t i = 0; i < directRoles.size(); ++i) {
                const User::RoleData& role(directRoles[i]);
                if (!role.hasRole)
                    continue;
                indirectRoles.insert(role.name);
                if (_roleGraphState == roleGraphStateConsistent) {
                    for (RoleNameIterator subordinates = _roleGraph.getIndirectSubordinates(
                                 role.name);
                         subordinates.more();
                         subordinates.next()) {

                        indirectRoles.insert(subordinates.get());
                    }
                }
                const PrivilegeVector& rolePrivileges(
                        (_roleGraphState == roleGraphStateConsistent) ?
                        _roleGraph.getAllPrivileges(role.name) :
                        _roleGraph.getDirectPrivileges(role.name));
                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 indirectRolesElement = resultDoc.makeElementArray("indirectRoles");
        mutablebson::Element privilegesElement = resultDoc.makeElementArray("privileges");
        mutablebson::Element warningsElement = resultDoc.makeElementArray("warnings");
        fassert(17158, resultDoc.root().pushBack(privilegesElement));
        fassert(17159, resultDoc.root().pushBack(indirectRolesElement));
        if (_roleGraphState != roleGraphStateConsistent) {
            fassert(17160, warningsElement.appendString(
                            "", "Role graph inconsistent, only direct privileges available."));
        }
        addRoleNameObjectsToArrayElement(indirectRolesElement,
                                         makeRoleNameIteratorForContainer(indirectRoles));
        addPrivilegeObjectsOrWarningsToArrayElement(
                privilegesElement, warningsElement, allPrivileges);
        if (warningsElement.hasChildren()) {
            fassert(17161, resultDoc.root().pushBack(warningsElement));
        }
        *result = resultDoc.getObject();
        return Status::OK();
    }
Status AuthzManagerExternalStateMongos::getUserDescription(OperationContext* opCtx,
                                                           const UserName& userName,
                                                           BSONObj* result) {
    if (!shouldUseRolesFromConnection(opCtx, userName)) {
        BSONObj usersInfoCmd =
            BSON("usersInfo" << BSON_ARRAY(BSON(AuthorizationManager::USER_NAME_FIELD_NAME
                                                << userName.getUser()
                                                << AuthorizationManager::USER_DB_FIELD_NAME
                                                << userName.getDB()))
                             << "showPrivileges"
                             << true
                             << "showCredentials"
                             << true);
        BSONObjBuilder builder;
        const bool ok = Grid::get(opCtx)->catalogClient()->runUserManagementReadCommand(
            opCtx, "admin", usersInfoCmd, &builder);
        BSONObj cmdResult = builder.obj();
        if (!ok) {
            return getStatusFromCommandResult(cmdResult);
        }

        std::vector<BSONElement> foundUsers = cmdResult["users"].Array();
        if (foundUsers.size() == 0) {
            return Status(ErrorCodes::UserNotFound,
                          "User \"" + userName.toString() + "\" not found");
        }

        if (foundUsers.size() > 1) {
            return Status(ErrorCodes::UserDataInconsistent,
                          str::stream() << "Found multiple users on the \"" << userName.getDB()
                                        << "\" database with name \""
                                        << userName.getUser()
                                        << "\"");
        }
        *result = foundUsers[0].Obj().getOwned();
        return Status::OK();
    } else {
        // Obtain privilege information from the config servers for all roles acquired from the X509
        // certificate.
        BSONArrayBuilder userRolesBuilder;
        auto& sslPeerInfo = SSLPeerInfo::forSession(opCtx->getClient()->session());
        for (const RoleName& role : sslPeerInfo.roles) {
            userRolesBuilder.append(BSON(AuthorizationManager::ROLE_NAME_FIELD_NAME
                                         << role.getRole()
                                         << AuthorizationManager::ROLE_DB_FIELD_NAME
                                         << role.getDB()));
        }
        BSONArray providedRoles = userRolesBuilder.arr();

        BSONObj rolesInfoCmd = BSON("rolesInfo" << providedRoles << "showPrivileges"
                                                << "asUserFragment");

        BSONObjBuilder cmdResultBuilder;
        const bool cmdOk = Grid::get(opCtx)->catalogClient()->runUserManagementReadCommand(
            opCtx, "admin", rolesInfoCmd, &cmdResultBuilder);
        BSONObj cmdResult = cmdResultBuilder.obj();
        if (!cmdOk || !cmdResult["userFragment"].ok()) {
            return Status(ErrorCodes::FailedToParse,
                          "Unable to get resolved X509 roles from config server: " +
                              getStatusFromCommandResult(cmdResult).toString());
        }
        cmdResult = cmdResult["userFragment"].Obj().getOwned();
        BSONElement userRoles = cmdResult["roles"];
        BSONElement userInheritedRoles = cmdResult["inheritedRoles"];
        BSONElement userInheritedPrivileges = cmdResult["inheritedPrivileges"];

        if (userRoles.eoo() || userInheritedRoles.eoo() || userInheritedPrivileges.eoo() ||
            !userRoles.isABSONObj() || !userInheritedRoles.isABSONObj() ||
            !userInheritedPrivileges.isABSONObj()) {
            return Status(
                ErrorCodes::UserDataInconsistent,
                "Recieved malformed response to request for X509 roles from config server");
        }

        *result = BSON("_id" << userName.getUser() << "user" << userName.getUser() << "db"
                             << userName.getDB()
                             << "credentials"
                             << BSON("external" << true)
                             << "roles"
                             << BSONArray(cmdResult["roles"].Obj())
                             << "inheritedRoles"
                             << BSONArray(cmdResult["inheritedRoles"].Obj())
                             << "inheritedPrivileges"
                             << BSONArray(cmdResult["inheritedPrivileges"].Obj()));
        return Status::OK();
    }
}