StatusWith<bool> SaslPLAINServerConversation::step(StringData inputData, std::string* outputData) {
    // Expecting user input on the form: user\0user\0pwd
    std::string input = inputData.toString();
    std::string pwd = "";

    try {
        _user = input.substr(0, inputData.find('\0'));
        pwd = input.substr(inputData.find('\0', _user.size() + 1) + 1);
    } catch (std::out_of_range& exception) {
        return StatusWith<bool>(ErrorCodes::AuthenticationFailed,
                                mongoutils::str::stream()
                                    << "Incorrectly formatted PLAIN client message");
    }

    User* userObj;
    // The authentication database is also the source database for the user.
    Status status =
        _saslAuthSession->getAuthorizationSession()->getAuthorizationManager().acquireUser(
            _saslAuthSession->getOpCtxt(),
            UserName(_user, _saslAuthSession->getAuthenticationDatabase()),
            &userObj);

    if (!status.isOK()) {
        return StatusWith<bool>(status);
    }

    const User::CredentialData creds = userObj->getCredentials();
    _saslAuthSession->getAuthorizationSession()->getAuthorizationManager().releaseUser(userObj);

    std::string authDigest = createPasswordDigest(_user, pwd);

    if (!creds.password.empty()) {
        // Handle schemaVersion26Final (MONGODB-CR/SCRAM mixed mode)
        if (authDigest != creds.password) {
            return StatusWith<bool>(ErrorCodes::AuthenticationFailed,
                                    mongoutils::str::stream() << "Incorrect user name or password");
        }
    } else {
        // Handle schemaVersion28SCRAM (SCRAM only mode)
        unsigned char storedKey[scram::hashSize];
        unsigned char serverKey[scram::hashSize];

        scram::generateSecrets(
            authDigest,
            reinterpret_cast<const unsigned char*>(base64::decode(creds.scram.salt).c_str()),
            16,
            creds.scram.iterationCount,
            storedKey,
            serverKey);
        if (creds.scram.storedKey !=
            base64::encode(reinterpret_cast<const char*>(storedKey), scram::hashSize)) {
            return StatusWith<bool>(ErrorCodes::AuthenticationFailed,
                                    mongoutils::str::stream() << "Incorrect user name or password");
        }
    }

    *outputData = "";

    return StatusWith<bool>(true);
}
Exemple #2
0
bool legalClientSystemNS(StringData ns) {
    if (ns == "local.system.replset")
        return true;

    if (ns.find(".system.users") != string::npos)
        return true;

    if (ns == "admin.system.roles")
        return true;
    if (ns == kConfigCollection)
        return true;
    if (ns == kLogicalTimeKeysCollection)
        return true;
    if (ns == "admin.system.new_users")
        return true;
    if (ns == "admin.system.backup_users")
        return true;

    if (ns.find(".system.js") != string::npos)
        return true;

    if (nsToCollectionSubstring(ns) == NamespaceString::kSystemDotViewsCollectionName)
        return true;

    return false;
}
Exemple #3
0
    Status Database::validateDBName( StringData dbname ) {

        if ( dbname.size() <= 0 )
            return Status( ErrorCodes::BadValue, "db name is empty" );

        if ( dbname.size() >= 64 )
            return Status( ErrorCodes::BadValue, "db name is too long" );

        if ( dbname.find( '.' ) != string::npos )
            return Status( ErrorCodes::BadValue, "db name cannot contain a ." );

        if ( dbname.find( ' ' ) != string::npos )
            return Status( ErrorCodes::BadValue, "db name cannot contain a space" );

#ifdef _WIN32
        static const char* windowsReservedNames[] = {
            "con", "prn", "aux", "nul",
            "com1", "com2", "com3", "com4", "com5", "com6", "com7", "com8", "com9",
            "lpt1", "lpt2", "lpt3", "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9"
        };

        string lower( dbname.toString() );
        std::transform( lower.begin(), lower.end(), lower.begin(), ::tolower );
        for ( size_t i = 0; i < (sizeof(windowsReservedNames) / sizeof(char*)); ++i ) {
            if ( lower == windowsReservedNames[i] ) {
                stringstream errorString;
                errorString << "db name \"" << dbname.toString() << "\" is a reserved name";
                return Status( ErrorCodes::BadValue, errorString.str() );
            }
        }
#endif

        return Status::OK();
    }
list<intrusive_ptr<DocumentSource>> DocumentSourceCount::createFromBson(
    BSONElement elem, const intrusive_ptr<ExpressionContext>& pExpCtx) {
    uassert(40156,
            str::stream() << "the count field must be a non-empty string",
            elem.type() == String);

    StringData elemString = elem.valueStringData();
    uassert(
        40157, str::stream() << "the count field must be a non-empty string", !elemString.empty());

    uassert(40158,
            str::stream() << "the count field cannot be a $-prefixed path",
            elemString[0] != '$');

    uassert(40159,
            str::stream() << "the count field cannot contain a null byte",
            elemString.find('\0') == string::npos);

    uassert(40160,
            str::stream() << "the count field cannot contain '.'",
            elemString.find('.') == string::npos);

    BSONObj groupObj = BSON("$group" << BSON("_id" << BSONNULL << elemString << BSON("$sum" << 1)));
    BSONObj projectObj = BSON("$project" << BSON("_id" << 0 << elemString << 1));

    auto groupSource = DocumentSourceGroup::createFromBson(groupObj.firstElement(), pExpCtx);
    auto projectSource = DocumentSourceProject::createFromBson(projectObj.firstElement(), pExpCtx);

    return {groupSource, projectSource};
}
bool KVCatalog::isUserDataIdent(StringData ident) const {
    // Indexes, collections, and temporary RecordStore idents are all candidates for dropping when
    // the storage engine's metadata does not align with the catalog metadata.
    return ident.find("index-") != std::string::npos || ident.find("index/") != std::string::npos ||
        ident.find("temp-") != std::string::npos ||
        ident.find("collection-") != std::string::npos ||
        ident.find("collection/") != std::string::npos;
}
Exemple #6
0
    bool legalClientSystemNS( const StringData& ns , bool write ) {
        if( ns == "local.system.replset" ) return true;

        if ( ns.find( ".system.users" ) != string::npos )
            return true;

        if ( ns == "admin.system.roles" ) return true;
        if ( ns == "admin.system.version" ) return true;
        if ( ns == "admin.system.new_users" ) return true;
        if ( ns == "admin.system.backup_users" ) return true;

        if ( ns.find( ".system.js" ) != string::npos ) return true;

        return false;
    }
    bool getCanonicalIndexField( StringData fullName, string* out ) {
        // check if fieldName contains ".$" or ".###" substrings (#=digit) and skip them
        // however do not skip the first field even if it meets these criteria

        if ( fullName.find( '.' ) == string::npos )
            return false;

        bool modified = false;

        StringBuilder buf;
        for ( size_t i=0; i<fullName.size(); i++ ) {

            char c = fullName[i];

            if ( c != '.' ) {
                buf << c;
                continue;
            }

            if ( i + 1 == fullName.size() ) {
                // ends with '.'
                buf << c;
                continue;
            }

            // check for ".$", skip if present
            if ( fullName[i+1] == '$' ) {
                // only do this if its not something like $a
                if ( i + 2 >= fullName.size() || fullName[i+2] == '.' ) {
                    i++;
                    modified = true;
                    continue;
                }
            }

            // check for ".###" for any number of digits (no letters)
            if ( isdigit( fullName[i+1] ) ) {
                size_t j = i;
                // skip digits
                while ( j+1 < fullName.size() && isdigit( fullName[j+1] ) )
                    j++;

                if ( j+1 == fullName.size() || fullName[j+1] == '.' ) {
                    // only digits found, skip forward
                    i = j;
                    modified = true;
                    continue;
                }
            }

            buf << c;
        }

        if ( !modified )
            return false;

        *out = buf.str();
        return true;
    }
std::ostream& MessageEventDetailsEncoder::encode(const MessageEventEphemeral& event,
                                                 std::ostream& os) {
    const auto maxLogSizeKB = getMaxLogSizeKB();

    const size_t maxLogSize = maxLogSizeKB * 1024;

    getDateFormatter()(os, event.getDate());
    os << ' ';

    const auto severity = event.getSeverity();
    os << severity.toStringDataCompact();
    os << ' ';

    LogComponent component = event.getComponent();
    os << component;
    os << ' ';

    StringData contextName = event.getContextName();
    if (!contextName.empty()) {
        os << '[' << contextName << "] ";
    }

    StringData msg = event.getMessage();

#ifdef _WIN32
    // We need to translate embedded Unix style line endings into Windows style endings.
    std::string tempstr;
    size_t embeddedNewLine = msg.find('\n');

    if (embeddedNewLine != std::string::npos) {
        tempstr = msg.toString().replace(embeddedNewLine, 1, "\r\n");

        embeddedNewLine = tempstr.find('\n', embeddedNewLine + 2);
        while (embeddedNewLine != std::string::npos) {
            tempstr = tempstr.replace(embeddedNewLine, 1, "\r\n");

            embeddedNewLine = tempstr.find('\n', embeddedNewLine + 2);
        }

        msg = tempstr;
    }
#endif

    if (event.isTruncatable() && msg.size() > maxLogSize) {
        os << "warning: log line attempted (" << msg.size() / 1024 << "kB) over max size ("
           << maxLogSizeKB << "kB), printing beginning and end ... ";
        os << msg.substr(0, maxLogSize / 3);
        os << " .......... ";
        os << msg.substr(msg.size() - (maxLogSize / 3));
    } else {
        os << msg;
    }

    if (!msg.endsWith(kEOL))
        os << kEOL;

    return os;
}
    bool MongoVersionRange::isInRange(const StringData& version) const {

        if (maxVersion == "") {
            // If a prefix of the version specified is excluded, the specified version is
            // excluded
            if (version.find(minVersion) == 0) return true;
        }
        else {
            // Range is inclusive, so make sure the end and beginning prefix excludes all
            // prefixed versions as above
            if (version.find(minVersion) == 0) return true;
            if (version.find(maxVersion) == 0) return true;
            if (versionCmp(minVersion, version) <= 0 && versionCmp(maxVersion, version) >= 0) {
                return true;
            }
        }

        return false;
    }
Exemple #10
0
    Status userAllowedWriteNS( const StringData& db, const StringData& coll ) {
        // validity checking

        if ( db.size() == 0 )
            return Status( ErrorCodes::BadValue, "db cannot be blank" );

        if ( !NamespaceString::validDBName( db ) )
            return Status( ErrorCodes::BadValue, "invalid db name" );

        if ( coll.size() == 0 )
            return Status( ErrorCodes::BadValue, "collection cannot be blank" );

        if ( !NamespaceString::validCollectionName( coll ) )
            return Status( ErrorCodes::BadValue, "invalid collection name" );

        if ( db.size() + 1 /* dot */ + coll.size() > Namespace::MaxNsColletionLen )
            return Status( ErrorCodes::BadValue,
                           str::stream()
                             << "fully qualified namespace " << db << '.' << coll << " is too long "
                             << "(max is " << Namespace::MaxNsColletionLen << " bytes)" );

        // check spceial areas

        if ( db == "system" )
            return Status( ErrorCodes::BadValue, "cannot use 'system' database" );


        if ( coll.startsWith( "system." ) ) {
            if ( coll == "system.indexes" ) return Status::OK();
            if ( coll == "system.js" ) return Status::OK();
            if ( coll == "system.profile" ) return Status::OK();
            if ( coll == "system.users" ) return Status::OK();
            if ( db == "admin" ) {
                if ( coll == "system.version" ) return Status::OK();
                if ( coll == "system.roles" ) return Status::OK();
                if ( coll == "system.new_users" ) return Status::OK();
                if ( coll == "system.backup_users" ) return Status::OK();
            }
            if ( db == "local" ) {
                if ( coll == "system.replset" ) return Status::OK();
            }
            return Status( ErrorCodes::BadValue,
                           str::stream() << "cannot write to '" << db << "." << coll << "'" );
        }

        // some special rules

        if ( coll.find( ".system." ) != string::npos ) {
            // this matches old (2.4 and older) behavior, but I'm not sure its a good idea
            return Status( ErrorCodes::BadValue,
                           str::stream() << "cannot write to '" << db << "." << coll << "'" );
        }

        return Status::OK();
    }
    StatusWith<bool> SaslPLAINServerConversation::step(const StringData& inputData, 
                                                       std::string* outputData) {
        // Expecting user input on the form: user\0user\0pwd
        std::string input = inputData.toString();
        std::string pwd = "";
 
        try {
            _user = input.substr(0, inputData.find('\0'));
            pwd = input.substr(inputData.find('\0', _user.size()+1)+1);
        }
        catch (std::out_of_range& exception) {
            return StatusWith<bool>(ErrorCodes::AuthenticationFailed,
                mongoutils::str::stream() << "Incorrectly formatted PLAIN client message");
        }

        User* userObj;
        // The authentication database is also the source database for the user.
        Status status = _saslAuthSession->getAuthorizationSession()->getAuthorizationManager().
                acquireUser(_saslAuthSession->getOpCtxt(),
                            UserName(_user, _saslAuthSession->getAuthenticationDatabase()),
                            &userObj);

        if (!status.isOK()) {
            return StatusWith<bool>(status);
        }

        const User::CredentialData creds = userObj->getCredentials();
        _saslAuthSession->getAuthorizationSession()->getAuthorizationManager().
                releaseUser(userObj);

        std::string authDigest = createPasswordDigest(_user, pwd);
 
        if (authDigest != creds.password) {
            return StatusWith<bool>(ErrorCodes::AuthenticationFailed,
                mongoutils::str::stream() << "Incorrect user name or password");
        }

        *outputData = "";

        return StatusWith<bool>(true);
    }
bool legalClientSystemNS(StringData ns, bool write) {
    if (ns == "local.system.replset")
        return true;

    if (ns.find(".system.users") != string::npos)
        return true;

    if (ns == "admin.system.roles")
        return true;
    if (ns == kConfigCollection)
        return true;
    if (ns == "admin.system.new_users")
        return true;
    if (ns == "admin.system.backup_users")
        return true;

    if (ns.find(".system.js") != string::npos)
        return true;

    return false;
}
Exemple #13
0
Status RegexMatchExpression::init(StringData path, StringData regex, StringData options) {
    if (regex.size() > MaxPatternSize) {
        return Status(ErrorCodes::BadValue, "Regular expression is too long");
    }

    if (regex.find('\0') != std::string::npos) {
        return Status(ErrorCodes::BadValue,
                      "Regular expression cannot contain an embedded null byte");
    }

    if (options.find('\0') != std::string::npos) {
        return Status(ErrorCodes::BadValue,
                      "Regular expression options string cannot contain an embedded null byte");
    }

    _regex = regex.toString();
    _flags = options.toString();
    _re.reset(new pcrecpp::RE(_regex.c_str(), flags2options(_flags.c_str())));

    return setPath(path);
}
    bool CmdAuthenticate::authenticateX509(const string& dbname,
                                           BSONObj& cmdObj, 
                                           string& errmsg, 
                                           BSONObjBuilder& result) {
        if(dbname != "$external") {
            errmsg = "X.509 authentication must always use the $external database.";
            result.append(saslCommandCodeFieldName, ErrorCodes::AuthenticationFailed);
            return false;
        }

        std::string user = cmdObj.getStringField("user");
        ClientBasic *client = ClientBasic::getCurrent();
        AuthorizationSession* authorizationSession = client->getAuthorizationSession();
        StringData subjectName = client->port()->getX509SubjectName();
        
        if (user != subjectName) {
            errmsg = "There is no x.509 client certificate matching the user.";
            result.append(saslCommandCodeFieldName, ErrorCodes::AuthenticationFailed);
            return false;
        }
        else {
            StringData srvSubjectName = getSSLManager()->getSubjectName();
            StringData srvClusterId = srvSubjectName.substr(0, srvSubjectName.find("/CN")+1);
            StringData peerClusterId = subjectName.substr(0, subjectName.find("/CN")+1);

            // Handle internal cluster member 
            if (srvClusterId == peerClusterId) {
                authorizationSession->grantInternalAuthorization(UserName(user, "$external"));
            }
            // Handle normal client authentication
            else {
                Principal* principal = new Principal(UserName(user, "$external"));
                principal->setImplicitPrivilegeAcquisition(true);
                authorizationSession->addAuthorizedPrincipal(principal);
            }
            result.append( "dbname" , dbname );
            result.append( "user" , user );
            return true;
        }
    }
BSONElement extractElementAtPath(const BSONObj& obj, StringData path) {
    BSONElement e = obj.getField(path);
    if (e.eoo()) {
        size_t dot_offset = path.find('.');
        if (dot_offset != std::string::npos) {
            StringData left = path.substr(0, dot_offset);
            StringData right = path.substr(dot_offset + 1);
            BSONObj sub = obj.getObjectField(left);
            return sub.isEmpty() ? BSONElement() : extractElementAtPath(sub, right);
        }
    }

    return e;
}
    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();
        }
    }
Exemple #17
0
    // static
    BSONObj IndexLegacy::adjustIndexSpecObject(const BSONObj& obj) {
        string pluginName = IndexNames::findPluginName(obj.getObjectField("key"));

        if (IndexNames::TEXT == pluginName) {
            StringData desc = cc().desc();
            if (desc.find("conn") == 0) {
                // this is to make sure we only complain for users
                // if you do get a text index created an a primary
                // want it to index on the secondary as well
                massert(16811, "text search not enabled", fts::isTextSearchEnabled() );
            }
            return fts::FTSSpec::fixSpec(obj);
        }

        return obj;
    }
Exemple #18
0
void ValueWriter::writeThis(BSONObjBuilder* b,
                            StringData sd,
                            ObjectWrapper::WriteFieldRecursionFrames* frames) {
    uassert(17279,
            str::stream() << "Exceeded depth limit of " << ObjectWrapper::kMaxWriteFieldDepth
                          << " when converting js object to BSON. Do you have a cycle?",
            frames->size() < ObjectWrapper::kMaxWriteFieldDepth);

    // Null char should be at the end, not in the string
    uassert(16985,
            str::stream() << "JavaScript property (name) contains a null char "
                          << "which is not allowed in BSON. "
                          << (_originalParent ? _originalParent->jsonString() : ""),
            (std::string::npos == sd.find('\0')));

    if (_value.isString()) {
        JSStringWrapper jsstr;
        b->append(sd, toStringData(&jsstr));
    } else if (_value.isNumber()) {
        double val = toNumber();

        // if previous type was integer, keep it
        int intval = static_cast<int>(val);

        if (val == intval && _originalParent) {
            // This makes copying an object of numbers O(n**2) :(
            BSONElement elmt = _originalParent->getField(sd);
            if (elmt.type() == mongo::NumberInt) {
                b->append(sd, intval);
                return;
            }
        }

        b->append(sd, val);
    } else if (_value.isObject()) {
        _writeObject(b, sd, frames);
    } else if (_value.isBoolean()) {
        b->appendBool(sd, _value.toBoolean());
    } else if (_value.isUndefined()) {
        b->appendUndefined(sd);
    } else if (_value.isNull()) {
        b->appendNull(sd);
    } else {
        uasserted(16662,
                  str::stream() << "unable to convert JavaScript property to mongo element " << sd);
    }
}
void WiredTigerKVEngine::_checkIdentPath(StringData ident) {
    size_t start = 0;
    size_t idx;
    while ((idx = ident.find('/', start)) != string::npos) {
        StringData dir = ident.substr(0, idx);

        boost::filesystem::path subdir = _path;
        subdir /= dir.toString();
        if (!boost::filesystem::exists(subdir)) {
            LOG(1) << "creating subdirectory: " << dir;
            try {
                boost::filesystem::create_directory(subdir);
            } catch (const std::exception& e) {
                error() << "error creating path " << subdir.string() << ' ' << e.what();
                throw;
            }
        }

        start = idx + 1;
    }
}
Exemple #20
0
        Status storageValid(const mb::ConstElement& elem, const bool deep) {
            if (!elem.ok())
                return Status(ErrorCodes::BadValue, "Invalid elements cannot be stored.");

            // Field names of elements inside arrays are not meaningful in mutable bson,
            // so we do not want to validate them.
            //
            // TODO: Revisit how mutable handles array field names. We going to need to make
            // this better if we ever want to support ordered updates that can alter the same
            // element repeatedly; see SERVER-12848.
            const mb::ConstElement& parent = elem.parent();
            const bool childOfArray = parent.ok() ? (parent.getType() == mongo::Array) : false;

            if (!childOfArray) {
                StringData fieldName = elem.getFieldName();
                // Cannot start with "$", unless dbref
                if (fieldName[0] == '$') {
                    Status status = validateDollarPrefixElement(elem, deep);
                    if (!status.isOK())
                        return status;
                }
                else if (fieldName.find(".") != string::npos) {
                    // Field name cannot have a "." in it.
                    return Status(ErrorCodes::DottedFieldName,
                                  str::stream() << "The dotted field '"
                                                << elem.getFieldName() << "' in '"
                                                << mb::getFullName(elem)
                                                << "' is not valid for storage.");
                }
            }

            if (deep) {
                // Check children if there are any.
                Status s = storageValidChildren(elem, deep);
                if (!s.isOK())
                    return s;
            }

            return Status::OK();
        }
Exemple #21
0
Status parseCertificateSelector(SSLParams::CertificateSelector* selector,
                                StringData name,
                                StringData value) {
    selector->subject.clear();
    selector->thumbprint.clear();

    const auto delim = value.find('=');
    if (delim == std::string::npos) {
        return {ErrorCodes::BadValue,
                str::stream() << "Certificate selector for '" << name
                              << "' must be a key=value pair"};
    }

    auto key = value.substr(0, delim);
    if (key == "subject") {
        selector->subject = value.substr(delim + 1).toString();
        return Status::OK();
    }

    if (key != "thumbprint") {
        return {ErrorCodes::BadValue,
                str::stream() << "Unknown certificate selector property for '" << name << "': '"
                              << key
                              << "'"};
    }

    auto swHex = hexToVector(value.substr(delim + 1));
    if (!swHex.isOK()) {
        return {ErrorCodes::BadValue,
                str::stream() << "Invalid certificate selector value for '" << name << "': "
                              << swHex.getStatus().reason()};
    }

    selector->thumbprint = std::move(swHex.getValue());

    return Status::OK();
}
Exemple #22
0
    // ns is either a full namespace or "dbname." when invalidating for a whole db
    void ClientCursor::invalidate(const StringData &ns) {
        Lock::assertWriteLocked(ns);
        size_t dotpos = ns.find('.');
        verify(dotpos != string::npos);
        bool isDB = (dotpos + 1) == ns.size(); // first (and only) dot is the last char

        {
            //cout << "\nTEMP invalidate " << ns << endl;
            Database *db = cc().database();
            verify(db);
            verify( ns.startsWith(db->name()) );

            for( LockedIterator i; i.ok(); ) {
                ClientCursor *cc = i.current();

                bool shouldDelete = false;
                if (cc->c()->shouldDestroyOnNSDeletion() && cc->_db == db) {
                    if (isDB) {
                        // already checked that db matched above
                        dassert( StringData(cc->_ns).startsWith(ns) );
                        shouldDelete = true;
                    }
                    else {
                        if ( ns == cc->_ns )
                            shouldDelete = true;
                    }
                }

                if ( shouldDelete ) {
                    i.deleteAndAdvance();
                }
                else {
                    i.advance();
                }
            }
        }
    }
StatusWith<bool> SaslPLAINServerConversation::step(StringData inputData, std::string* outputData) {
    if (_saslAuthSession->getAuthenticationDatabase() == "$external") {
        return Status(ErrorCodes::AuthenticationFailed,
                      "PLAIN mechanism must be used with internal users");
    }

    // Expecting user input on the form: [authz-id]\0authn-id\0pwd
    std::string input = inputData.toString();

    SecureAllocatorAuthDomain::SecureString pwd = "";
    try {
        size_t firstNull = inputData.find('\0');
        if (firstNull == std::string::npos) {
            return Status(
                ErrorCodes::AuthenticationFailed,
                str::stream()
                    << "Incorrectly formatted PLAIN client message, missing first NULL delimiter");
        }
        size_t secondNull = inputData.find('\0', firstNull + 1);
        if (secondNull == std::string::npos) {
            return Status(
                ErrorCodes::AuthenticationFailed,
                str::stream()
                    << "Incorrectly formatted PLAIN client message, missing second NULL delimiter");
        }

        std::string authorizationIdentity = input.substr(0, firstNull);
        _user = input.substr(firstNull + 1, (secondNull - firstNull) - 1);
        if (_user.empty()) {
            return Status(ErrorCodes::AuthenticationFailed,
                          str::stream()
                              << "Incorrectly formatted PLAIN client message, empty username");
        } else if (!authorizationIdentity.empty() && authorizationIdentity != _user) {
            return Status(ErrorCodes::AuthenticationFailed,
                          str::stream()
                              << "SASL authorization identity must match authentication identity");
        }
        pwd = SecureAllocatorAuthDomain::SecureString(input.substr(secondNull + 1).c_str());
        if (pwd->empty()) {
            return Status(ErrorCodes::AuthenticationFailed,
                          str::stream()
                              << "Incorrectly formatted PLAIN client message, empty password");
        }
    } catch (std::out_of_range&) {
        return Status(ErrorCodes::AuthenticationFailed,
                      mongoutils::str::stream() << "Incorrectly formatted PLAIN client message");
    }

    User* userObj;
    // The authentication database is also the source database for the user.
    Status status =
        _saslAuthSession->getAuthorizationSession()->getAuthorizationManager().acquireUser(
            _saslAuthSession->getOpCtxt(),
            UserName(_user, _saslAuthSession->getAuthenticationDatabase()),
            &userObj);

    if (!status.isOK()) {
        return status;
    }

    const auto creds = userObj->getCredentials();
    _saslAuthSession->getAuthorizationSession()->getAuthorizationManager().releaseUser(userObj);

    *outputData = "";
    const auto sha256Status = trySCRAM<SHA256Block>(creds, pwd->c_str());
    if (!sha256Status.isOK()) {
        return sha256Status;
    }
    if (sha256Status.getValue()) {
        return true;
    }

    const auto authDigest = createPasswordDigest(_user, pwd->c_str());
    const auto sha1Status = trySCRAM<SHA1Block>(creds, authDigest);
    if (!sha1Status.isOK()) {
        return sha1Status;
    }
    if (sha1Status.getValue()) {
        return true;
    }

    return Status(ErrorCodes::AuthenticationFailed, str::stream() << "No credentials available.");
}
Exemple #24
0
    bool processObj(const BSONObj &obj) {
        if (obj.hasField("$err")) {
            log() << "error getting oplog: " << obj << endl;
            return false;
        }

        static const char *names[] = {"ts", "op", "ns", "o", "b"};
        BSONElement fields[5];
        obj.getFields(5, names, fields);

        BSONElement &tsElt = fields[0];
        if (!tsElt.ok()) {
            log() << "oplog format error: " << obj << " missing 'ts' field." << endl;
            return false;
        }
        if (tsElt.type() != Date && tsElt.type() != Timestamp) {
            log() << "oplog format error: " << obj << " wrong 'ts' field type." << endl;
            return false;
        }
        _thisTime = OpTime(tsElt.date());

        BSONElement &opElt = fields[1];
        if (!opElt.ok()) {
            log() << "oplog format error: " << obj << " missing 'op' field." << endl;
            return false;
        }
        StringData op = opElt.Stringdata();

        // nop
        if (op == "n") {
            return true;
        }
        // "presence of a database"
        if (op == "db") {
            return true;
        }
        if (op != "c" && op != "i" && op != "u" && op != "d") {
            log() << "oplog format error: " << obj << " has an invalid 'op' field of '" << op << "'." << endl;
            return false;
        }

        if (op != "i" && !_insertBuf.empty()) {
            flushInserts();
        }

        BSONElement &nsElt = fields[2];
        if (!nsElt.ok()) {
            log() << "oplog format error: " << obj << " missing 'ns' field." << endl;
            return false;
        }
        StringData ns = nsElt.Stringdata();
        size_t i = ns.find('.');
        if (i == string::npos) {
            log() << "oplog format error: invalid namespace '" << ns << "' in op " << obj << "." << endl;
            return false;
        }
        StringData dbname = ns.substr(0, i);
        StringData collname = ns.substr(i + 1);

        BSONElement &oElt = fields[3];
        if (!oElt.ok()) {
            log() << "oplog format error: " << obj << " missing 'o' field." << endl;
            return false;
        }
        BSONObj o = obj["o"].Obj();

        if (op == "c") {
            if (collname != "$cmd") {
                log() << "oplog format error: invalid namespace '" << ns << "' for command in op " << obj << "." << endl;
                return false;
            }
            BSONObj info;
            bool ok = _conn.runCommand(dbname.toString(), o, info);
            if (!ok) {
                StringData fieldName = o.firstElementFieldName();
                BSONElement errmsgElt = info["errmsg"];
                StringData errmsg = errmsgElt.type() == String ? errmsgElt.Stringdata() : "";
                bool isDropIndexes = (fieldName == "dropIndexes" || fieldName == "deleteIndexes");
                if (((fieldName == "drop" || isDropIndexes) && errmsg == "ns not found") ||
                    (isDropIndexes && (errmsg == "index not found" || errmsg.find("can't find index with key:") == 0))) {
                    // This is actually ok.  We don't mind dropping something that's not there.
                    LOG(1) << "Tried to replay " << o << ", got " << info << ", ignoring." << endl;
                }
                else {
                    log() << "replay of command " << o << " failed: " << info << endl;
                    return false;
                }
            }
        }
        else {
            string nsstr = ns.toString();
            if (op == "i") {
                if (collname == "system.indexes") {
                    // Can't ensure multiple indexes in the same batch.
                    flushInserts();

                    // For now, we need to strip out any background fields from
                    // ensureIndex.  Once we do hot indexing we can do something more
                    // like what vanilla applyOperation_inlock does.
                    if (o["background"].trueValue()) {
                        BSONObjBuilder builder;
                        BSONObjIterator it(o);
                        while (it.more()) {
                            BSONElement e = it.next();
                            if (strncmp(e.fieldName(), "background", sizeof("background")) != 0) {
                                builder.append(e);
                            }
                        }
                        o = builder.obj();
                    }
                    // We need to warn very carefully about dropDups.
                    if (o["dropDups"].trueValue()) {
                        BSONObjBuilder builder;
                        BSONObjIterator it(o);
                        while (it.more()) {
                            BSONElement e = it.next();
                            if (strncmp(e.fieldName(), "dropDups", sizeof("dropDups")) != 0) {
                                builder.append(e);
                            }
                        }
                        warning() << "Detected an ensureIndex with dropDups: true in " << o << "." << endl;
                        warning() << "This option is not supported in TokuMX, because it deletes arbitrary data." << endl;
                        warning() << "If it were replayed, it could result in a completely different data set than the source database." << endl;
                        warning() << "We will attempt to replay it without dropDups, but if that fails, you must restart your migration process." << endl;
                        _conn.insert(nsstr, o);
                        string err = _conn.getLastError(dbname.toString(), false, false);
                        if (!err.empty()) {
                            log() << "replay of operation " << obj << " failed: " << err << endl;
                            warning() << "You cannot continue processing this replication stream.  You need to restart the migration process." << endl;
                            _running = false;
                            _logAtExit = false;
                            return true;
                        }
                    }
                }
                pushInsert(nsstr, o);
                // Don't call GLE or update _maxOpTimeSynced yet.
                _thisTime = OpTime();
                return true;
            }
            else if (op == "u") {
                BSONElement o2Elt = obj["o2"];
                if (!o2Elt.ok()) {
                    log() << "oplog format error: " << obj << " missing 'o2' field." << endl;
                    return false;
                }
                BSONElement &bElt = fields[4];
                bool upsert = bElt.booleanSafe();
                BSONObj o2 = o2Elt.Obj();
                _conn.update(nsstr, o2, o, upsert, false);
            }
            else if (op == "d") {
                BSONElement &bElt = fields[4];
                bool justOne = bElt.booleanSafe();
                _conn.remove(nsstr, o, justOne);
            }
            string err = _conn.getLastError(dbname.toString(), false, false);
            if (!err.empty()) {
                log() << "replay of operation " << obj << " failed: " << err << endl;
                return false;
            }
        }

        // If we got here, we completed the operation successfully.
        _maxOpTimeSynced = _thisTime;
        _thisTime = OpTime();
        return true;
    }
StatusWith<bool> SaslPLAINServerConversation::step(StringData inputData, std::string* outputData) {
    if (_saslAuthSession->getAuthenticationDatabase() == "$external") {
        return Status(ErrorCodes::AuthenticationFailed,
                      "PLAIN mechanism must be used with internal users");
    }

    // Expecting user input on the form: [authz-id]\0authn-id\0pwd
    std::string input = inputData.toString();

    SecureAllocatorAuthDomain::SecureString pwd = "";
    try {
        size_t firstNull = inputData.find('\0');
        if (firstNull == std::string::npos) {
            return Status(
                ErrorCodes::AuthenticationFailed,
                str::stream()
                    << "Incorrectly formatted PLAIN client message, missing first NULL delimiter");
        }
        size_t secondNull = inputData.find('\0', firstNull + 1);
        if (secondNull == std::string::npos) {
            return Status(
                ErrorCodes::AuthenticationFailed,
                str::stream()
                    << "Incorrectly formatted PLAIN client message, missing second NULL delimiter");
        }

        std::string authorizationIdentity = input.substr(0, firstNull);
        _user = input.substr(firstNull + 1, (secondNull - firstNull) - 1);
        if (_user.empty()) {
            return Status(ErrorCodes::AuthenticationFailed,
                          str::stream()
                              << "Incorrectly formatted PLAIN client message, empty username");
        } else if (!authorizationIdentity.empty() && authorizationIdentity != _user) {
            return Status(ErrorCodes::AuthenticationFailed,
                          str::stream()
                              << "SASL authorization identity must match authentication identity");
        }
        pwd = SecureAllocatorAuthDomain::SecureString(input.substr(secondNull + 1).c_str());
        if (pwd->empty()) {
            return Status(ErrorCodes::AuthenticationFailed,
                          str::stream()
                              << "Incorrectly formatted PLAIN client message, empty password");
        }
    } catch (std::out_of_range& exception) {
        return Status(ErrorCodes::AuthenticationFailed,
                      mongoutils::str::stream() << "Incorrectly formatted PLAIN client message");
    }

    User* userObj;
    // The authentication database is also the source database for the user.
    Status status =
        _saslAuthSession->getAuthorizationSession()->getAuthorizationManager().acquireUser(
            _saslAuthSession->getOpCtxt(),
            UserName(_user, _saslAuthSession->getAuthenticationDatabase()),
            &userObj);

    if (!status.isOK()) {
        return StatusWith<bool>(status);
    }

    const User::CredentialData creds = userObj->getCredentials();
    _saslAuthSession->getAuthorizationSession()->getAuthorizationManager().releaseUser(userObj);

    std::string authDigest = createPasswordDigest(_user, pwd->c_str());

    if (!creds.password.empty()) {
        // Handle schemaVersion26Final (MONGODB-CR/SCRAM mixed mode)
        if (authDigest != creds.password) {
            return StatusWith<bool>(ErrorCodes::AuthenticationFailed,
                                    mongoutils::str::stream() << "Incorrect user name or password");
        }
    } else {
        // Handle schemaVersion28SCRAM (SCRAM only mode)
        std::string decodedSalt = base64::decode(creds.scram.salt);
        scram::SCRAMSecrets secrets = scram::generateSecrets(scram::SCRAMPresecrets(
            authDigest,
            std::vector<std::uint8_t>(reinterpret_cast<const std::uint8_t*>(decodedSalt.c_str()),
                                      reinterpret_cast<const std::uint8_t*>(decodedSalt.c_str()) +
                                          16),
            creds.scram.iterationCount));
        if (creds.scram.storedKey !=
            base64::encode(reinterpret_cast<const char*>(secrets->storedKey.data()),
                           secrets->storedKey.size())) {
            return StatusWith<bool>(ErrorCodes::AuthenticationFailed,
                                    mongoutils::str::stream() << "Incorrect user name or password");
        }
    }

    *outputData = "";

    return StatusWith<bool>(true);
}
Exemple #26
0
    Status HostAndPort::initialize(const StringData& s) {
        size_t colonPos = s.rfind(':');
        StringData hostPart = s.substr(0, colonPos);

        // handle ipv6 hostPart (which we require to be wrapped in []s)
        const size_t openBracketPos = s.find('[');
        const size_t closeBracketPos = s.find(']');
        if (openBracketPos != std::string::npos) {
            if (openBracketPos != 0) {
                return Status(ErrorCodes::FailedToParse,
                              str::stream() << "'[' present, but not first character in "
                                            << s.toString());
            }
            if (closeBracketPos == std::string::npos) {
                return Status(ErrorCodes::FailedToParse,
                              str::stream() << "ipv6 address is missing closing ']' in hostname in "
                                            << s.toString());
            }

            hostPart = s.substr(openBracketPos+1, closeBracketPos-openBracketPos-1);
            // prevent accidental assignment of port to the value of the final portion of hostPart
            if (colonPos < closeBracketPos) {
                colonPos = std::string::npos;
            }
            else if (colonPos != closeBracketPos+1) {
                return Status(ErrorCodes::FailedToParse,
                              str::stream() << "Extraneous characters between ']' and pre-port ':'"
                                            << " in " << s.toString());
            }
        }
        else if (closeBracketPos != std::string::npos) {
            return Status(ErrorCodes::FailedToParse,
                          str::stream() << "']' present without '[' in " << s.toString());
        }

        if (hostPart.empty()) {
            return Status(ErrorCodes::FailedToParse, str::stream() <<
                          "Empty host component parsing HostAndPort from \"" <<
                          escape(s.toString()) << "\"");
        }

        int port;
        if (colonPos != std::string::npos) {
            const StringData portPart = s.substr(colonPos + 1);
            Status status = parseNumberFromStringWithBase(portPart, 10, &port);
            if (!status.isOK()) {
                return status;
            }
            if (port <= 0) {
                return Status(ErrorCodes::FailedToParse, str::stream() << "Port number " << port <<
                              " out of range parsing HostAndPort from \"" << escape(s.toString()) <<
                              "\"");
            }
        }
        else {
            port = -1;
        }
        _host = hostPart.toString();
        _port = port;
        return Status::OK();
    }
Exemple #27
0
    void ClientCursor::invalidate(const StringData& ns) {
        Lock::assertWriteLocked(ns);

        size_t dot = ns.find( '.' );
        verify( dot != string::npos );

        // first (and only) dot is the last char
        bool isDB = dot == ns.size() - 1;

        Database *db = cc().database();
        verify(db);
        verify(ns.startsWith(db->name()));

        recursive_scoped_lock cclock(ccmutex);
        // Look at all active non-cached Runners.  These are the runners that are in auto-yield mode
        // that are not attached to the the client cursor. For example, all internal runners don't
        // need to be cached -- there will be no getMore.
        for (set<Runner*>::iterator it = nonCachedRunners.begin(); it != nonCachedRunners.end();
             ++it) {

            Runner* runner = *it;
            const string& runnerNS = runner->ns();
            if ( ( isDB && StringData(runnerNS).startsWith(ns) ) || ns == runnerNS ) {
                runner->kill();
            }
        }

        // Look at all cached ClientCursor(s).  The CC may have a Runner, a Cursor, or nothing (see
        // sharding_block.h).
        CCById::const_iterator it = clientCursorsById.begin();
        while (it != clientCursorsById.end()) {
            ClientCursor* cc = it->second;

            // We're only interested in cursors over one db.
            if (cc->_db != db) {
                ++it;
                continue;
            }

            // Note that a valid ClientCursor state is "no cursor no runner."  This is because
            // the set of active cursor IDs in ClientCursor is used as representation of query
            // state.  See sharding_block.h.  TODO(greg,hk): Move this out.
            if (NULL == cc->c() && NULL == cc->_runner.get()) {
                ++it;
                continue;
            }

            bool shouldDelete = false;

            // We will only delete CCs with runners that are not actively in use.  The runners that
            // are actively in use are instead kill()-ed.
            if (NULL != cc->_runner.get()) {
                verify(NULL == cc->c());

                if (isDB || cc->_runner->ns() == ns) {
                    // If there is a pinValue >= 100, somebody is actively using the CC and we do
                    // not delete it.  Instead we notify the holder that we killed it.  The holder
                    // will then delete the CC.
                    if (cc->_pinValue >= 100) {
                        cc->_runner->kill();
                    }
                    else {
                        // pinvalue is <100, so there is nobody actively holding the CC.  We can
                        // safely delete it as nobody is holding the CC.
                        shouldDelete = true;
                    }
                }
            }
            // Begin cursor-only DEPRECATED
            else if (cc->c()->shouldDestroyOnNSDeletion()) {
                verify(NULL == cc->_runner.get());

                if (isDB) {
                    // already checked that db matched above
                    dassert( StringData(cc->_ns).startsWith( ns ) );
                    shouldDelete = true;
                }
                else {
                    if ( ns == cc->_ns ) {
                        shouldDelete = true;
                    }
                }
            }
            // End cursor-only DEPRECATED

            if (shouldDelete) {
                ClientCursor* toDelete = it->second;
                CursorId id = toDelete->cursorid();
                delete toDelete;
                // We're not following the usual paradigm of saving it, ++it, and deleting the saved
                // 'it' because deleting 'it' might invalidate the next thing in clientCursorsById.
                // TODO: Why?
                it = clientCursorsById.upper_bound(id);
            }
            else {
                ++it;
            }
        }
    }
void BtreeKeyGeneratorV1::getKeysImplWithArray(
    std::vector<const char*> fieldNames,
    std::vector<BSONElement> fixed,
    const BSONObj& obj,
    BSONObjSet* keys,
    unsigned numNotFound,
    const std::vector<PositionalPathInfo>& positionalInfo,
    MultikeyPaths* multikeyPaths) const {
    BSONElement arrElt;

    // A set containing the position of any indexed fields in the key pattern that traverse through
    // the 'arrElt' array value.
    std::set<size_t> arrIdxs;

    // A vector with size equal to the number of elements in the index key pattern. Each element in
    // the vector, if initialized, refers to the component within the indexed field that traverses
    // through the 'arrElt' array value. We say that this component within the indexed field
    // corresponds to a path that causes the index to be multikey if the 'arrElt' array value
    // contains multiple elements.
    //
    // For example, consider the index {'a.b': 1, 'a.c'} and the document
    // {a: [{b: 1, c: 'x'}, {b: 2, c: 'y'}]}. The path "a" causes the index to be multikey, so we'd
    // have a std::vector<boost::optional<size_t>>{{0U}, {0U}}.
    //
    // Furthermore, due to how positional key patterns are specified, it's possible for an indexed
    // field to cause the index to be multikey at a different component than another indexed field
    // that also traverses through the 'arrElt' array value. It's then also possible for an indexed
    // field not to cause the index to be multikey, even if it traverses through the 'arrElt' array
    // value, because only a particular element would be indexed.
    //
    // For example, consider the index {'a.b': 1, 'a.b.0'} and the document {a: {b: [1, 2]}}. The
    // path "a.b" causes the index to be multikey, but the key pattern "a.b.0" only indexes the
    // first element of the array, so we'd have a
    // std::vector<boost::optional<size_t>>{{1U}, boost::none}.
    std::vector<boost::optional<size_t>> arrComponents(fieldNames.size());

    bool mayExpandArrayUnembedded = true;
    for (size_t i = 0; i < fieldNames.size(); ++i) {
        if (*fieldNames[i] == '\0') {
            continue;
        }

        bool arrayNestedArray;
        // Extract element matching fieldName[ i ] from object xor array.
        BSONElement e =
            extractNextElement(obj, positionalInfo[i], &fieldNames[i], &arrayNestedArray);

        if (e.eoo()) {
            // if field not present, set to null
            fixed[i] = nullElt;
            // done expanding this field name
            fieldNames[i] = "";
            numNotFound++;
        } else if (e.type() == Array) {
            arrIdxs.insert(i);
            if (arrElt.eoo()) {
                // we only expand arrays on a single path -- track the path here
                arrElt = e;
            } else if (e.rawdata() != arrElt.rawdata()) {
                // enforce single array path here
                assertParallelArrays(e.fieldName(), arrElt.fieldName());
            }
            if (arrayNestedArray) {
                mayExpandArrayUnembedded = false;
            }
        } else {
            // not an array - no need for further expansion
            fixed[i] = e;
        }
    }

    if (arrElt.eoo()) {
        // No array, so generate a single key.
        if (_isSparse && numNotFound == fieldNames.size()) {
            return;
        }
        BSONObjBuilder b(_sizeTracker);
        for (std::vector<BSONElement>::iterator i = fixed.begin(); i != fixed.end(); ++i) {
            CollationIndexKey::collationAwareIndexKeyAppend(*i, _collator, &b);
        }
        keys->insert(b.obj());
    } else if (arrElt.embeddedObject().firstElement().eoo()) {
        // We've encountered an empty array.
        if (multikeyPaths && mayExpandArrayUnembedded) {
            // Any indexed path which traverses through the empty array must be recorded as an array
            // component.
            for (auto i : arrIdxs) {
                // We need to determine which component of the indexed field causes the index to be
                // multikey as a result of the empty array. Indexed empty arrays are considered
                // multikey and may occur mid-path. For instance, the indexed path "a.b.c" has
                // multikey components {0, 1} given the document {a: [{b: []}, {b: 1}]}.
                size_t fullPathLength = _pathLengths[i];
                size_t suffixPathLength = FieldRef{fieldNames[i]}.numParts();
                invariant(suffixPathLength < fullPathLength);
                arrComponents[i] = fullPathLength - suffixPathLength - 1;
            }
        }

        // For an empty array, set matching fields to undefined.
        _getKeysArrEltFixed(&fieldNames,
                            &fixed,
                            undefinedElt,
                            keys,
                            numNotFound,
                            arrElt,
                            arrIdxs,
                            true,
                            _emptyPositionalInfo,
                            multikeyPaths);
    } else {
        BSONObj arrObj = arrElt.embeddedObject();

        // For positional key patterns, e.g. {'a.1.b': 1}, we lookup the indexed array element
        // and then traverse the remainder of the field path up front. This prevents us from
        // having to look up the indexed element again on each recursive call (i.e. once per
        // array element).
        std::vector<PositionalPathInfo> subPositionalInfo(fixed.size());
        for (size_t i = 0; i < fieldNames.size(); ++i) {
            const bool fieldIsArray = arrIdxs.find(i) != arrIdxs.end();

            if (*fieldNames[i] == '\0') {
                // We've reached the end of the path.
                if (multikeyPaths && fieldIsArray && mayExpandArrayUnembedded) {
                    // The 'arrElt' array value isn't expanded into multiple elements when the last
                    // component of the indexed field is positional and 'arrElt' contains nested
                    // array values. In all other cases, the 'arrElt' array value may be expanded
                    // into multiple element and can therefore cause the index to be multikey.
                    arrComponents[i] = _pathLengths[i] - 1;
                }
                continue;
            }

            // The earlier call to dps::extractElementAtPathOrArrayAlongPath(..., fieldNames[i])
            // modified fieldNames[i] to refer to the suffix of the path immediately following the
            // 'arrElt' array value. If we haven't reached the end of this indexed field yet, then
            // we must have traversed through 'arrElt'.
            invariant(fieldIsArray);

            StringData part = fieldNames[i];
            part = part.substr(0, part.find('.'));
            subPositionalInfo[i].positionallyIndexedElt = arrObj[part];
            if (subPositionalInfo[i].positionallyIndexedElt.eoo()) {
                // We aren't indexing a particular element of the 'arrElt' array value, so it may be
                // expanded into multiple elements. It can therefore cause the index to be multikey.
                if (multikeyPaths) {
                    // We need to determine which component of the indexed field causes the index to
                    // be multikey as a result of the 'arrElt' array value. Since
                    //
                    //   NumComponents("<pathPrefix>") + NumComponents("<pathSuffix>")
                    //       = NumComponents("<pathPrefix>.<pathSuffix>"),
                    //
                    // we can compute the number of components in a prefix of the indexed field by
                    // subtracting the number of components in the suffix 'fieldNames[i]' from the
                    // number of components in the indexed field '_fieldNames[i]'.
                    //
                    // For example, consider the indexed field "a.b.c" and the suffix "c". The path
                    // "a.b.c" has 3 components and the suffix "c" has 1 component. Subtracting the
                    // latter from the former yields the number of components in the prefix "a.b",
                    // i.e. 2.
                    size_t fullPathLength = _pathLengths[i];
                    size_t suffixPathLength = FieldRef{fieldNames[i]}.numParts();
                    invariant(suffixPathLength < fullPathLength);
                    arrComponents[i] = fullPathLength - suffixPathLength - 1;
                }
                continue;
            }

            // We're indexing an array element by its position. Traverse the remainder of the
            // field path now.
            //
            // Indexing an array element by its position selects a particular element of the
            // 'arrElt' array value when generating keys. It therefore cannot cause the index to be
            // multikey.
            subPositionalInfo[i].arrayObj = arrObj;
            subPositionalInfo[i].remainingPath = fieldNames[i];
            subPositionalInfo[i].dottedElt = dps::extractElementAtPathOrArrayAlongPath(
                arrObj, subPositionalInfo[i].remainingPath);
        }

        // Generate a key for each element of the indexed array.
        for (const auto arrObjElem : arrObj) {
            _getKeysArrEltFixed(&fieldNames,
                                &fixed,
                                arrObjElem,
                                keys,
                                numNotFound,
                                arrElt,
                                arrIdxs,
                                mayExpandArrayUnembedded,
                                subPositionalInfo,
                                multikeyPaths);
        }
    }

    // Record multikey path components.
    if (multikeyPaths) {
        for (size_t i = 0; i < arrComponents.size(); ++i) {
            if (auto arrComponent = arrComponents[i]) {
                (*multikeyPaths)[i].insert(*arrComponent);
            }
        }
    }
}
Exemple #29
0
bool KVCatalog::isUserDataIdent(StringData ident) const {
    return ident.find("index-") != std::string::npos || ident.find("index/") != std::string::npos ||
        ident.find("collection-") != std::string::npos ||
        ident.find("collection/") != std::string::npos;
}
Exemple #30
0
bool hasFunctionIdentifier(StringData code) {
    if (code.size() < 9 || code.find("function") != 0)
        return false;

    return code[8] == ' ' || code[8] == '(';
}