Status V2UserDocumentParser::initializeAuthenticationRestrictionsFromUserDocument( const BSONObj& privDoc, User* user) const { RestrictionDocuments::sequence_type restrictionVector; // Restrictions on the user const auto authenticationRestrictions = privDoc[AUTHENTICATION_RESTRICTIONS_FIELD_NAME]; if (!authenticationRestrictions.eoo()) { if (authenticationRestrictions.type() != Array) { return Status(ErrorCodes::UnsupportedFormat, "'authenticationRestrictions' field must be an array"); } auto restrictions = parseAuthenticationRestriction(BSONArray(authenticationRestrictions.Obj())); if (!restrictions.isOK()) { return restrictions.getStatus(); } restrictionVector.push_back(restrictions.getValue()); } // Restrictions from roles const auto inherited = privDoc[INHERITED_AUTHENTICATION_RESTRICTIONS_FIELD_NAME]; if (!inherited.eoo()) { if (inherited.type() != Array) { return Status(ErrorCodes::UnsupportedFormat, "'inheritedAuthenticationRestrictions' field must be an array"); } for (const auto& roleRestriction : BSONArray(inherited.Obj())) { if (roleRestriction.type() != Array) { return Status(ErrorCodes::UnsupportedFormat, "'inheritedAuthenticationRestrictions' sub-fields must be arrays"); } auto roleRestrictionDoc = parseAuthenticationRestriction(BSONArray(roleRestriction.Obj())); if (!roleRestrictionDoc.isOK()) { return roleRestrictionDoc.getStatus(); } restrictionVector.push_back(roleRestrictionDoc.getValue()); } } if (user) { user->setRestrictions(RestrictionDocuments(restrictionVector)); } return Status::OK(); }
LegacyReply::LegacyReply(const Message* message) : _message(std::move(message)) { invariant(message->operation() == opReply); QueryResult::View qr = _message->singleData().view2ptr(); // should be checked by caller. invariant(qr.msgdata().getNetworkOp() == opReply); uassert(ErrorCodes::BadValue, str::stream() << "Got legacy command reply with a bad cursorId field," << " expected a value of 0 but got " << qr.getCursorId(), qr.getCursorId() == 0); uassert(ErrorCodes::BadValue, str::stream() << "Got legacy command reply with a bad nReturned field," << " expected a value of 1 but got " << qr.getNReturned(), qr.getNReturned() == 1); uassert(ErrorCodes::BadValue, str::stream() << "Got legacy command reply with a bad startingFrom field," << " expected a value of 0 but got " << qr.getStartingFrom(), qr.getStartingFrom() == 0); std::tie(_commandReply, _metadata) = uassertStatusOK(rpc::upconvertReplyMetadata(BSONObj(qr.data()))); // Copy the bson array of documents from the message into // a contiguous area of memory owned by _docBuffer so // DocumentRange can be used to iterate over documents auto cursorElem = _commandReply[LegacyReplyBuilder::kCursorTag]; if (cursorElem.eoo()) return; BSONObj cursorObj = cursorElem.Obj(); auto firstBatchElem = cursorObj[LegacyReplyBuilder::kFirstBatchTag]; if (firstBatchElem.eoo()) return; for (BSONObjIterator it(firstBatchElem.Obj()); it.more(); it.next()) { invariant((*it).isABSONObj()); BSONObj doc = (*it).Obj(); doc.appendSelfToBufBuilder(_docBuffer); } const char* dataBegin = _docBuffer.buf(); const char* dataEnd = dataBegin + _docBuffer.len(); _outputDocs = DocumentRange(dataBegin, dataEnd); return; }
StatusWith<ServerSelectionMetadata> ServerSelectionMetadata::readFromMetadata( const BSONObj& metadata) { auto secondaryOkField = metadata.getField(kSecondaryOkFieldName); bool secondaryOk = !secondaryOkField.eoo(); boost::optional<ReadPreferenceSetting> readPreference; BSONElement rpElem; auto readPrefExtractStatus = bsonExtractTypedField(metadata, kReadPreferenceFieldName, mongo::Object, &rpElem); if (readPrefExtractStatus == ErrorCodes::NoSuchKey) { // Do nothing, it's valid to have no ReadPreference } else if (!readPrefExtractStatus.isOK()) { return readPrefExtractStatus; } else { // We have a read preference in the metadata object. auto parsedRps = ReadPreferenceSetting::fromBSON(rpElem.Obj()); if (!parsedRps.isOK()) { return parsedRps.getStatus(); } readPreference.emplace(std::move(parsedRps.getValue())); } return ServerSelectionMetadata(secondaryOk, std::move(readPreference)); }
Status LogicalTimeMetadataHook::readReplyMetadata(OperationContext* opCtx, StringData replySource, const BSONObj& metadataObj) { auto parseStatus = LogicalTimeMetadata::readFromMetadata(metadataObj); if (!parseStatus.isOK()) { return parseStatus.getStatus(); } auto& signedTime = parseStatus.getValue().getSignedTime(); // LogicalTimeMetadata is default constructed if no cluster time metadata was sent, so a // default constructed SignedLogicalTime should be ignored. if (signedTime.getTime() == LogicalTime::kUninitialized || !LogicalClock::get(_service)->isEnabled()) { return Status::OK(); } if (opCtx) { auto timeTracker = OperationTimeTracker::get(opCtx); auto operationTime = metadataObj[kOperationTimeFieldName]; if (!operationTime.eoo()) { invariant(operationTime.type() == BSONType::bsonTimestamp); timeTracker->updateOperationTime(LogicalTime(operationTime.timestamp())); } } return LogicalClock::get(_service)->advanceClusterTime(signedTime.getTime()); }
/** * Returns true if request is a query for sharded indexes. */ static bool doShardedIndexQuery(OperationContext* txn, Request& r, const QuerySpec& qSpec) { // Extract the ns field from the query, which may be embedded within the "query" or // "$query" field. auto nsField = qSpec.filter()["ns"]; if (nsField.eoo()) { return false; } const NamespaceString indexNSSQuery(nsField.str()); auto status = grid.catalogCache()->getDatabase(txn, indexNSSQuery.db().toString()); if (!status.isOK()) { return false; } shared_ptr<DBConfig> config = status.getValue(); if (!config->isSharded(indexNSSQuery.ns())) { return false; } // if you are querying on system.indexes, we need to make sure we go to a shard // that actually has chunks. This is not a perfect solution (what if you just // look at all indexes), but better than doing nothing. ShardPtr shard; ChunkManagerPtr cm; config->getChunkManagerOrPrimary(indexNSSQuery.ns(), cm, shard); if (cm) { set<ShardId> shardIds; cm->getAllShardIds(&shardIds); verify(shardIds.size() > 0); shard = grid.shardRegistry()->getShard(*shardIds.begin()); } ShardConnection dbcon(shard->getConnString(), r.getns()); DBClientBase& c = dbcon.conn(); string actualServer; Message response; bool ok = c.call(r.m(), response, true, &actualServer); uassert(10200, "mongos: error calling db", ok); { QueryResult::View qr = response.singleData().view2ptr(); if (qr.getResultFlags() & ResultFlag_ShardConfigStale) { dbcon.done(); // Version is zero b/c this is deprecated codepath throw RecvStaleConfigException(r.getns(), "Strategy::doQuery", ChunkVersion(0, 0, OID()), ChunkVersion(0, 0, OID())); } } r.reply(response, actualServer.size() ? actualServer : c.getServerAddress()); dbcon.done(); return true; }
int solve(int si) { int ret=0,i; for(i=0;i<N;i++) { ret = (ret + eoo( abs(si-idx[i]))) & 1; } return ret; }
void ASSERT_APPENDED_VALUE(ServerParameter* sp, Validator validator) { BSONObjBuilder b; sp->append(nullptr, b, sp->name()); auto obj = b.obj(); ASSERT_EQ(obj.nFields(), 1); auto elem = obj[sp->name()]; ASSERT_FALSE(elem.eoo()); validator(elem); }
Status ServerSelectionMetadata::downconvert(const BSONObj& command, const BSONObj& metadata, BSONObjBuilder* legacyCommand, int* legacyQueryFlags) { auto ssmElem = metadata.getField(fieldName()); if (ssmElem.eoo()) { // slaveOk is false by default. *legacyQueryFlags &= ~mongo::QueryOption_SlaveOk; legacyCommand->appendElements(command); return Status::OK(); } else if (ssmElem.type() != mongo::Object) { return { ErrorCodes::TypeMismatch, str::stream() << "ServerSelectionMetadata metadata element must be an object, but got " << typeName(ssmElem.type())}; } auto ssmObj = ssmElem.Obj(); BSONElement secondaryOkElem; BSONElement readPreferenceElem; for (auto&& el : ssmObj) { auto fname = el.fieldNameStringData(); if (fname == kSecondaryOkFieldName) { secondaryOkElem = std::move(el); } else if (fname == kReadPreferenceFieldName) { readPreferenceElem = std::move(el); } } if (!secondaryOkElem.eoo() && secondaryOkElem.trueValue()) { *legacyQueryFlags |= mongo::QueryOption_SlaveOk; } else { *legacyQueryFlags &= ~mongo::QueryOption_SlaveOk; } if (!readPreferenceElem.eoo()) { // Use 'query' to wrap query, then append read preference. // NOTE(amidvidy): Oddly, the _isSecondaryQuery implementation in dbclient_rs does // not unwrap the query properly - it only checks for 'query', and not // '$query'. We should probably standardize on one - drivers use '$query', // and the shell uses 'query'. See SERVER-18705 for details. // TODO: this may need to use the $queryOptions hack on mongos. legacyCommand->append(kQueryWrapper, command); legacyCommand->append(readPreferenceElem); } else { legacyCommand->appendElements(command); } return Status::OK(); }
void ReplicationRecoveryImpl::_truncateOplogTo(OperationContext* opCtx, Timestamp truncateTimestamp) { Timer timer; const NamespaceString oplogNss(NamespaceString::kRsOplogNamespace); AutoGetDb autoDb(opCtx, oplogNss.db(), MODE_IX); Lock::CollectionLock oplogCollectionLoc(opCtx->lockState(), oplogNss.ns(), MODE_X); Collection* oplogCollection = autoDb.getDb()->getCollection(opCtx, oplogNss); if (!oplogCollection) { fassertFailedWithStatusNoTrace( 34418, Status(ErrorCodes::NamespaceNotFound, str::stream() << "Can't find " << NamespaceString::kRsOplogNamespace.ns())); } // Scan through oplog in reverse, from latest entry to first, to find the truncateTimestamp. RecordId oldestIDToDelete; // Non-null if there is something to delete. auto oplogRs = oplogCollection->getRecordStore(); auto oplogReverseCursor = oplogRs->getCursor(opCtx, /*forward=*/false); size_t count = 0; while (auto next = oplogReverseCursor->next()) { const BSONObj entry = next->data.releaseToBson(); const RecordId id = next->id; count++; const auto tsElem = entry["ts"]; if (count == 1) { if (tsElem.eoo()) LOG(2) << "Oplog tail entry: " << redact(entry); else LOG(2) << "Oplog tail entry ts field: " << tsElem; } if (tsElem.timestamp() < truncateTimestamp) { // If count == 1, that means that we have nothing to delete because everything in the // oplog is < truncateTimestamp. if (count != 1) { invariant(!oldestIDToDelete.isNull()); oplogCollection->cappedTruncateAfter(opCtx, oldestIDToDelete, /*inclusive=*/true); } log() << "Replication recovery oplog truncation finished in: " << timer.millis() << "ms"; return; } oldestIDToDelete = id; } severe() << "Reached end of oplog looking for oplog entry before " << truncateTimestamp.toBSON() << " but couldn't find any after looking through " << count << " entries."; fassertFailedNoTrace(40296); }
Status ReadAfterOpTimeArgs::initialize(const BSONObj& cmdObj) { auto afterElem = cmdObj[ReadAfterOpTimeArgs::kRootFieldName]; if (afterElem.eoo()) { return Status::OK(); } if (!afterElem.isABSONObj()) { return Status(ErrorCodes::FailedToParse, "'after' field should be an object"); } BSONObj readAfterObj = afterElem.Obj(); BSONElement opTimeElem; auto opTimeStatus = bsonExtractTypedField( readAfterObj, ReadAfterOpTimeArgs::kOpTimeFieldName, Object, &opTimeElem); if (!opTimeStatus.isOK()) { return opTimeStatus; } BSONObj opTimeObj = opTimeElem.Obj(); BSONElement timestampElem; Timestamp timestamp; auto timestampStatus = bsonExtractTimestampField( opTimeObj, ReadAfterOpTimeArgs::kOpTimestampFieldName, ×tamp); if (!timestampStatus.isOK()) { return timestampStatus; } long long termNumber; auto termStatus = bsonExtractIntegerField(opTimeObj, ReadAfterOpTimeArgs::kOpTermFieldName, &termNumber); if (!termStatus.isOK()) { return termStatus; } _opTime = OpTime(timestamp, termNumber); return Status::OK(); }
Status ReadConcernArgs::initialize(const BSONObj& cmdObj) { auto readConcernElem = cmdObj[ReadConcernArgs::kReadConcernFieldName]; if (readConcernElem.eoo()) { return Status::OK(); } if (!readConcernElem.isABSONObj()) { return Status(ErrorCodes::FailedToParse, str::stream() << kReadConcernFieldName << " field should be an object"); } BSONObj readConcernObj = readConcernElem.Obj(); if (readConcernObj.hasField(kOpTimeFieldName)) { OpTime opTime; auto opTimeStatus = bsonExtractOpTimeField(readConcernObj, kOpTimeFieldName, &opTime); if (!opTimeStatus.isOK()) { return opTimeStatus; } _opTime = opTime; } std::string levelString; auto readCommittedStatus = bsonExtractStringField(readConcernObj, kLevelFieldName, &levelString); if (readCommittedStatus.isOK()) { if (levelString == kLocalReadConcernStr) { _level = ReadConcernLevel::kLocalReadConcern; } else if (levelString == kMajorityReadConcernStr) { _level = ReadConcernLevel::kMajorityReadConcern; } else { return Status(ErrorCodes::FailedToParse, str::stream() << kReadConcernFieldName << '.' << kLevelFieldName << " must be either \"local\" or \"majority\""); } } else if (readCommittedStatus != ErrorCodes::NoSuchKey) { return readCommittedStatus; } return Status::OK(); }
Status CommittedOpTimeMetadataHook::readReplyMetadata(OperationContext* opCtx, StringData replySource, const BSONObj& metadataObj) { auto lastCommittedOpTimeField = metadataObj[kLastCommittedOpTimeFieldName]; if (lastCommittedOpTimeField.eoo()) { return Status::OK(); } invariant(lastCommittedOpTimeField.type() == BSONType::bsonTimestamp); // replySource is the HostAndPort of a single server, except when this hook is triggered // through DBClientReplicaSet, when it will be a replica set connection string. The // shardRegistry stores connection strings and hosts in its lookup table, in addition to shard // ids, so replySource can be correctly passed on to ShardRegistry::getShardNoReload. auto shard = Grid::get(_service)->shardRegistry()->getShardNoReload(replySource.toString()); if (shard) { shard->updateLastCommittedOpTime(LogicalTime(lastCommittedOpTimeField.timestamp())); } return Status::OK(); }
Status V2UserDocumentParser::checkValidUserDocument(const BSONObj& doc) const { auto userIdElement = doc[AuthorizationManager::USERID_FIELD_NAME]; auto userElement = doc[AuthorizationManager::USER_NAME_FIELD_NAME]; auto userDBElement = doc[AuthorizationManager::USER_DB_FIELD_NAME]; auto credentialsElement = doc[CREDENTIALS_FIELD_NAME]; auto rolesElement = doc[ROLES_FIELD_NAME]; // Validate the "userId" element. if (!userIdElement.eoo()) { if (!userIdElement.isBinData(BinDataType::newUUID)) { return _badValue("User document needs 'userId' field to be a UUID"); } } // Validate the "user" element. if (userElement.type() != String) return _badValue("User document needs 'user' field to be a string"); if (userElement.valueStringData().empty()) return _badValue("User document needs 'user' field to be non-empty"); // Validate the "db" element if (userDBElement.type() != String || userDBElement.valueStringData().empty()) { return _badValue("User document needs 'db' field to be a non-empty string"); } StringData userDBStr = userDBElement.valueStringData(); if (!NamespaceString::validDBName(userDBStr, NamespaceString::DollarInDbNameBehavior::Allow) && userDBStr != "$external") { return _badValue(mongoutils::str::stream() << "'" << userDBStr << "' is not a valid value for the db field."); } // Validate the "credentials" element if (credentialsElement.eoo()) { return _badValue("User document needs 'credentials' object"); } if (credentialsElement.type() != Object) { return _badValue("User document needs 'credentials' field to be an object"); } BSONObj credentialsObj = credentialsElement.Obj(); if (credentialsObj.isEmpty()) { return _badValue("User document needs 'credentials' field to be a non-empty object"); } if (userDBStr == "$external") { BSONElement externalElement = credentialsObj[MONGODB_EXTERNAL_CREDENTIAL_FIELD_NAME]; if (externalElement.eoo() || externalElement.type() != Bool || !externalElement.Bool()) { return _badValue( "User documents for users defined on '$external' must have " "'credentials' field set to {external: true}"); } } else { const auto validateScram = [&credentialsObj](const auto& fieldName) { auto scramElement = credentialsObj[fieldName]; if (scramElement.eoo()) { return Status(ErrorCodes::NoSuchKey, str::stream() << fieldName << " does not exist"); } if (scramElement.type() != Object) { return _badValue(str::stream() << fieldName << " credential must be an object, if present"); } return Status::OK(); }; const auto sha1status = validateScram(SCRAMSHA1_CREDENTIAL_FIELD_NAME); if (!sha1status.isOK() && (sha1status.code() != ErrorCodes::NoSuchKey)) { return sha1status; } const auto sha256status = validateScram(SCRAMSHA256_CREDENTIAL_FIELD_NAME); if (!sha256status.isOK() && (sha256status.code() != ErrorCodes::NoSuchKey)) { return sha256status; } if (!sha1status.isOK() && !sha256status.isOK()) { return _badValue( "User document must provide credentials for all " "non-external users"); } } // Validate the "roles" element. Status status = _checkV2RolesArray(rolesElement); if (!status.isOK()) return status; // Validate the "authenticationRestrictions" element. status = initializeAuthenticationRestrictionsFromUserDocument(doc, nullptr); if (!status.isOK()) { return status; } return Status::OK(); }
virtual bool run(OperationContext* opCtx, const string&, const BSONObj& cmdObj, BSONObjBuilder& result) { /* currently request to arbiter is (somewhat arbitrarily) an ismaster request that is not authenticated. */ if (cmdObj["forShell"].trueValue()) { LastError::get(opCtx->getClient()).disable(); } transport::Session::TagMask sessionTagsToSet = 0; transport::Session::TagMask sessionTagsToUnset = 0; // Tag connections to avoid closing them on stepdown. auto hangUpElement = cmdObj["hangUpOnStepDown"]; if (!hangUpElement.eoo() && !hangUpElement.trueValue()) { sessionTagsToSet |= transport::Session::kKeepOpen; } auto& clientMetadataIsMasterState = ClientMetadataIsMasterState::get(opCtx->getClient()); bool seenIsMaster = clientMetadataIsMasterState.hasSeenIsMaster(); if (!seenIsMaster) { clientMetadataIsMasterState.setSeenIsMaster(); } BSONElement element = cmdObj[kMetadataDocumentName]; if (!element.eoo()) { if (seenIsMaster) { uasserted(ErrorCodes::ClientMetadataCannotBeMutated, "The client metadata document may only be sent in the first isMaster"); } auto swParseClientMetadata = ClientMetadata::parse(element); uassertStatusOK(swParseClientMetadata.getStatus()); invariant(swParseClientMetadata.getValue()); swParseClientMetadata.getValue().get().logClientMetadata(opCtx->getClient()); clientMetadataIsMasterState.setClientMetadata( opCtx->getClient(), std::move(swParseClientMetadata.getValue())); } // Parse the optional 'internalClient' field. This is provided by incoming connections from // mongod and mongos. auto internalClientElement = cmdObj["internalClient"]; if (internalClientElement) { sessionTagsToSet |= transport::Session::kInternalClient; uassert(ErrorCodes::TypeMismatch, str::stream() << "'internalClient' must be of type Object, but was of type " << typeName(internalClientElement.type()), internalClientElement.type() == BSONType::Object); bool foundMaxWireVersion = false; for (auto&& elem : internalClientElement.Obj()) { auto fieldName = elem.fieldNameStringData(); if (fieldName == "minWireVersion") { // We do not currently use 'internalClient.minWireVersion'. continue; } else if (fieldName == "maxWireVersion") { foundMaxWireVersion = true; uassert(ErrorCodes::TypeMismatch, str::stream() << "'maxWireVersion' field of 'internalClient' must be " "of type int, but was of type " << typeName(elem.type()), elem.type() == BSONType::NumberInt); // All incoming connections from mongod/mongos of earlier versions should be // closed if the featureCompatibilityVersion is bumped to 3.6. if (elem.numberInt() >= WireSpec::instance().incomingInternalClient.maxWireVersion) { sessionTagsToSet |= transport::Session::kLatestVersionInternalClientKeepOpen; } else { sessionTagsToUnset |= transport::Session::kLatestVersionInternalClientKeepOpen; } } else { uasserted(ErrorCodes::BadValue, str::stream() << "Unrecognized field of 'internalClient': '" << fieldName << "'"); } } uassert(ErrorCodes::BadValue, "Missing required field 'maxWireVersion' of 'internalClient'", foundMaxWireVersion); } else { sessionTagsToUnset |= (transport::Session::kInternalClient | transport::Session::kLatestVersionInternalClientKeepOpen); sessionTagsToSet |= transport::Session::kExternalClientKeepOpen; } auto session = opCtx->getClient()->session(); if (session) { session->mutateTags( [sessionTagsToSet, sessionTagsToUnset](transport::Session::TagMask originalTags) { // After a mongos sends the initial "isMaster" command with its mongos client // information, it sometimes sends another "isMaster" command that is forwarded // from its client. Once kInternalClient has been set, we assume that any future // "isMaster" commands are forwarded in this manner, and we do not update the // session tags. if ((originalTags & transport::Session::kInternalClient) == 0) { return (originalTags | sessionTagsToSet) & ~sessionTagsToUnset; } else { return originalTags; } }); } appendReplicationInfo(opCtx, result, 0); if (serverGlobalParams.clusterRole == ClusterRole::ConfigServer) { const int configServerModeNumber = 2; result.append("configsvr", configServerModeNumber); } result.appendNumber("maxBsonObjectSize", BSONObjMaxUserSize); result.appendNumber("maxMessageSizeBytes", MaxMessageSizeBytes); result.appendNumber("maxWriteBatchSize", write_ops::kMaxWriteBatchSize); result.appendDate("localTime", jsTime()); result.append("logicalSessionTimeoutMinutes", localLogicalSessionTimeoutMinutes); result.appendNumber("connectionId", opCtx->getClient()->getConnectionId()); if (internalClientElement) { result.append("minWireVersion", WireSpec::instance().incomingInternalClient.minWireVersion); result.append("maxWireVersion", WireSpec::instance().incomingInternalClient.maxWireVersion); } else { result.append("minWireVersion", WireSpec::instance().incomingExternalClient.minWireVersion); result.append("maxWireVersion", WireSpec::instance().incomingExternalClient.maxWireVersion); } result.append("readOnly", storageGlobalParams.readOnly); const auto parameter = mapFindWithDefault(ServerParameterSet::getGlobal()->getMap(), "automationServiceDescriptor", static_cast<ServerParameter*>(nullptr)); if (parameter) parameter->append(opCtx, result, "automationServiceDescriptor"); if (opCtx->getClient()->session()) { MessageCompressorManager::forSession(opCtx->getClient()->session()) .serverNegotiate(cmdObj, &result); } auto& saslMechanismRegistry = SASLServerMechanismRegistry::get(opCtx->getServiceContext()); saslMechanismRegistry.advertiseMechanismNamesForUser(opCtx, cmdObj, &result); return true; }