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(txn) ->runUserManagementReadCommand(txn, "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 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 AuthorizationManager::_buildPrivilegeSetFromOldStylePrivilegeDocument( const std::string& dbname, const UserName& user, const BSONObj& privilegeDocument, PrivilegeSet* result) const { if (!(privilegeDocument.hasField(AuthorizationManager::USER_NAME_FIELD_NAME) && privilegeDocument.hasField(AuthorizationManager::PASSWORD_FIELD_NAME))) { return Status(ErrorCodes::UnsupportedFormat, mongoutils::str::stream() << "Invalid old-style privilege document " "received when trying to extract privileges: " << privilegeDocument, 0); } std::string userName = privilegeDocument[AuthorizationManager::USER_NAME_FIELD_NAME].str(); if (userName != user.getUser()) { return Status(ErrorCodes::BadValue, mongoutils::str::stream() << "Principal name from privilege document \"" << userName << "\" doesn't match name of provided Principal \"" << user.getUser() << "\"", 0); } bool readOnly = privilegeDocument[READONLY_FIELD_NAME].trueValue(); ActionSet actions = getActionsForOldStyleUser(dbname, readOnly); std::string resourceName = (dbname == ADMIN_DBNAME || dbname == LOCAL_DBNAME) ? PrivilegeSet::WILDCARD_RESOURCE : dbname; result->grantPrivilege(Privilege(resourceName, actions), user); 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 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 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 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(); }
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(); } }
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 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; }
int main() { UserName test; vector<string> coll; coll.push_back("MasterOfDisaster"); coll.push_back("DingBat"); coll.push_back("Orpheus"); coll.push_back("WolfMan"); coll.push_back("MrKnowItAll"); coll.push_back("TygerTyger"); coll.push_back("TygerTyger1"); cout << test.newMember(coll, "TygerTyger") << endl; return EXIT_SUCCESS; }
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); }
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 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(); } }
// BEGIN KAWIGIEDIT TESTING // Generated by KawigiEdit 2.1.4 (beta) modified by pivanof bool KawigiEdit_RunTest(int testNum, vector <string> p0, string p1, bool hasAnswer, string p2) { cout << "Test " << testNum << ": [" << "{"; for (int i = 0; int(p0.size()) > i; ++i) { if (i > 0) { cout << ","; } cout << "\"" << p0[i] << "\""; } cout << "}" << "," << "\"" << p1 << "\""; cout << "]" << endl; UserName *obj; string answer; obj = new UserName(); clock_t startTime = clock(); answer = obj->newMember(p0, p1); clock_t endTime = clock(); delete obj; bool res; res = true; cout << "Time: " << double(endTime - startTime) / CLOCKS_PER_SEC << " seconds" << endl; if (hasAnswer) { cout << "Desired answer:" << endl; cout << "\t" << "\"" << p2 << "\"" << endl; } cout << "Your answer:" << endl; cout << "\t" << "\"" << answer << "\"" << endl; if (hasAnswer) { res = answer == p2; } if (!res) { cout << "DOESN'T MATCH!!!!" << endl; } else if (double(endTime - startTime) / CLOCKS_PER_SEC >= 2) { cout << "FAIL the timeout" << endl; res = false; } else if (hasAnswer) { cout << "Match :-)" << endl; } else { cout << "OK, but is it right?" << endl; } cout << "" << endl; return res; }
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 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 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::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(); } }
StatusWith<UserHandle> AuthorizationManagerImpl::acquireUserForSessionRefresh( OperationContext* opCtx, const UserName& userName, const User::UserId& uid) { auto swUserHandle = acquireUser(opCtx, userName); if (!swUserHandle.isOK()) { return swUserHandle.getStatus(); } auto ret = std::move(swUserHandle.getValue()); if (uid != ret->getID()) { return {ErrorCodes::UserNotFound, str::stream() << "User id from privilege document '" << userName.toString() << "' does not match user id in session."}; } return ret; }
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); }