Handle mongodb auth errors that may occur after running the server for a while (#4576)

This commit is contained in:
Florent Vilmart
2018-03-02 10:24:58 -05:00
committed by GitHub
parent 9db63a4a50
commit 60eea59c76

View File

@@ -23,6 +23,7 @@ import Parse from 'parse/node';
// @flow-disable-next // @flow-disable-next
import _ from 'lodash'; import _ from 'lodash';
import defaults from '../../../defaults'; import defaults from '../../../defaults';
import logger from '../../../logger';
// @flow-disable-next // @flow-disable-next
const mongodb = require('mongodb'); const mongodb = require('mongodb');
@@ -160,6 +161,16 @@ export class MongoStorageAdapter implements StorageAdapter {
return this.connectionPromise; return this.connectionPromise;
} }
handleError<T>(error: ?Error): Promise<T> {
if (error && error.code === 13) { // Unauthorized error
delete this.client;
delete this.database;
delete this.connectionPromise;
logger.error('Received unauthorized error', { error: error });
}
throw error;
}
handleShutdown() { handleShutdown() {
if (!this.client) { if (!this.client) {
return; return;
@@ -170,7 +181,8 @@ export class MongoStorageAdapter implements StorageAdapter {
_adaptiveCollection(name: string) { _adaptiveCollection(name: string) {
return this.connect() return this.connect()
.then(() => this.database.collection(this._collectionPrefix + name)) .then(() => this.database.collection(this._collectionPrefix + name))
.then(rawCollection => new MongoCollection(rawCollection)); .then(rawCollection => new MongoCollection(rawCollection))
.catch(err => this.handleError(err));
} }
_schemaCollection(): Promise<MongoSchemaCollection> { _schemaCollection(): Promise<MongoSchemaCollection> {
@@ -184,14 +196,14 @@ export class MongoStorageAdapter implements StorageAdapter {
return this.database.listCollections({ name: this._collectionPrefix + name }).toArray(); return this.database.listCollections({ name: this._collectionPrefix + name }).toArray();
}).then(collections => { }).then(collections => {
return collections.length > 0; return collections.length > 0;
}); }).catch(err => this.handleError(err));
} }
setClassLevelPermissions(className: string, CLPs: any): Promise<void> { setClassLevelPermissions(className: string, CLPs: any): Promise<void> {
return this._schemaCollection() return this._schemaCollection()
.then(schemaCollection => schemaCollection.updateSchema(className, { .then(schemaCollection => schemaCollection.updateSchema(className, {
$set: { '_metadata.class_permissions': CLPs } $set: { '_metadata.class_permissions': CLPs }
})); })).catch(err => this.handleError(err));
} }
setIndexesWithSchemaFormat(className: string, submittedIndexes: any, existingIndexes: any = {}, fields: any): Promise<void> { setIndexesWithSchemaFormat(className: string, submittedIndexes: any, existingIndexes: any = {}, fields: any): Promise<void> {
@@ -237,7 +249,8 @@ export class MongoStorageAdapter implements StorageAdapter {
.then(() => this._schemaCollection()) .then(() => this._schemaCollection())
.then(schemaCollection => schemaCollection.updateSchema(className, { .then(schemaCollection => schemaCollection.updateSchema(className, {
$set: { '_metadata.indexes': existingIndexes } $set: { '_metadata.indexes': existingIndexes }
})); }))
.catch(err => this.handleError(err));
} }
setIndexesFromMongo(className: string) { setIndexesFromMongo(className: string) {
@@ -257,7 +270,9 @@ export class MongoStorageAdapter implements StorageAdapter {
.then(schemaCollection => schemaCollection.updateSchema(className, { .then(schemaCollection => schemaCollection.updateSchema(className, {
$set: { '_metadata.indexes': indexes } $set: { '_metadata.indexes': indexes }
})); }));
}).catch(() => { })
.catch(err => this.handleError(err))
.catch(() => {
// Ignore if collection not found // Ignore if collection not found
return Promise.resolve(); return Promise.resolve();
}); });
@@ -269,13 +284,15 @@ export class MongoStorageAdapter implements StorageAdapter {
mongoObject._id = className; mongoObject._id = className;
return this.setIndexesWithSchemaFormat(className, schema.indexes, {}, schema.fields) return this.setIndexesWithSchemaFormat(className, schema.indexes, {}, schema.fields)
.then(() => this._schemaCollection()) .then(() => this._schemaCollection())
.then(schemaCollection => schemaCollection.insertSchema(mongoObject)); .then(schemaCollection => schemaCollection.insertSchema(mongoObject))
.catch(err => this.handleError(err));
} }
addFieldIfNotExists(className: string, fieldName: string, type: any): Promise<void> { addFieldIfNotExists(className: string, fieldName: string, type: any): Promise<void> {
return this._schemaCollection() return this._schemaCollection()
.then(schemaCollection => schemaCollection.addFieldIfNotExists(className, fieldName, type)) .then(schemaCollection => schemaCollection.addFieldIfNotExists(className, fieldName, type))
.then(() => this.createIndexesIfNeeded(className, fieldName, type)); .then(() => this.createIndexesIfNeeded(className, fieldName, type))
.catch(err => this.handleError(err));
} }
// Drops a collection. Resolves with true if it was a Parse Schema (eg. _User, Custom, etc.) // Drops a collection. Resolves with true if it was a Parse Schema (eg. _User, Custom, etc.)
@@ -293,12 +310,14 @@ export class MongoStorageAdapter implements StorageAdapter {
// We've dropped the collection, now remove the _SCHEMA document // We've dropped the collection, now remove the _SCHEMA document
.then(() => this._schemaCollection()) .then(() => this._schemaCollection())
.then(schemaCollection => schemaCollection.findAndDeleteSchema(className)) .then(schemaCollection => schemaCollection.findAndDeleteSchema(className))
.catch(err => this.handleError(err));
} }
// Delete all data known to this adapter. Used for testing. // Delete all data known to this adapter. Used for testing.
deleteAllClasses() { deleteAllClasses() {
return storageAdapterAllCollections(this) return storageAdapterAllCollections(this)
.then(collections => Promise.all(collections.map(collection => collection.drop()))); .then(collections => Promise.all(collections.map(collection => collection.drop())))
.catch(err => this.handleError(err));
} }
// Remove the column and all the data. For Relations, the _Join collection is handled // Remove the column and all the data. For Relations, the _Join collection is handled
@@ -342,14 +361,16 @@ export class MongoStorageAdapter implements StorageAdapter {
return this._adaptiveCollection(className) return this._adaptiveCollection(className)
.then(collection => collection.updateMany({}, collectionUpdate)) .then(collection => collection.updateMany({}, collectionUpdate))
.then(() => this._schemaCollection()) .then(() => this._schemaCollection())
.then(schemaCollection => schemaCollection.updateSchema(className, schemaUpdate)); .then(schemaCollection => schemaCollection.updateSchema(className, schemaUpdate))
.catch(err => this.handleError(err));
} }
// Return a promise for all schemas known to this adapter, in Parse format. In case the // Return a promise for all schemas known to this adapter, in Parse format. In case the
// schemas cannot be retrieved, returns a promise that rejects. Requirements for the // schemas cannot be retrieved, returns a promise that rejects. Requirements for the
// rejection reason are TBD. // rejection reason are TBD.
getAllClasses(): Promise<StorageClass[]> { getAllClasses(): Promise<StorageClass[]> {
return this._schemaCollection().then(schemasCollection => schemasCollection._fetchAllSchemasFrom_SCHEMA()); return this._schemaCollection().then(schemasCollection => schemasCollection._fetchAllSchemasFrom_SCHEMA())
.catch(err => this.handleError(err));
} }
// Return a promise for the schema with the given name, in Parse format. If // Return a promise for the schema with the given name, in Parse format. If
@@ -358,6 +379,7 @@ export class MongoStorageAdapter implements StorageAdapter {
getClass(className: string): Promise<StorageClass> { getClass(className: string): Promise<StorageClass> {
return this._schemaCollection() return this._schemaCollection()
.then(schemasCollection => schemasCollection._fetchOneSchemaFrom_SCHEMA(className)) .then(schemasCollection => schemasCollection._fetchOneSchemaFrom_SCHEMA(className))
.catch(err => this.handleError(err));
} }
// TODO: As yet not particularly well specified. Creates an object. Maybe shouldn't even need the schema, // TODO: As yet not particularly well specified. Creates an object. Maybe shouldn't even need the schema,
@@ -381,7 +403,8 @@ export class MongoStorageAdapter implements StorageAdapter {
throw err; throw err;
} }
throw error; throw error;
}); })
.catch(err => this.handleError(err));
} }
// Remove all objects that match the given Parse Query. // Remove all objects that match the given Parse Query.
@@ -394,6 +417,7 @@ export class MongoStorageAdapter implements StorageAdapter {
const mongoWhere = transformWhere(className, query, schema); const mongoWhere = transformWhere(className, query, schema);
return collection.deleteMany(mongoWhere) return collection.deleteMany(mongoWhere)
}) })
.catch(err => this.handleError(err))
.then(({ result }) => { .then(({ result }) => {
if (result.n === 0) { if (result.n === 0) {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found.'); throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found.');
@@ -410,7 +434,8 @@ export class MongoStorageAdapter implements StorageAdapter {
const mongoUpdate = transformUpdate(className, update, schema); const mongoUpdate = transformUpdate(className, update, schema);
const mongoWhere = transformWhere(className, query, schema); const mongoWhere = transformWhere(className, query, schema);
return this._adaptiveCollection(className) return this._adaptiveCollection(className)
.then(collection => collection.updateMany(mongoWhere, mongoUpdate)); .then(collection => collection.updateMany(mongoWhere, mongoUpdate))
.catch(err => this.handleError(err));
} }
// Atomically finds and updates an object based on query. // Atomically finds and updates an object based on query.
@@ -427,7 +452,8 @@ export class MongoStorageAdapter implements StorageAdapter {
throw new Parse.Error(Parse.Error.DUPLICATE_VALUE, 'A duplicate value for a field with unique values was provided'); throw new Parse.Error(Parse.Error.DUPLICATE_VALUE, 'A duplicate value for a field with unique values was provided');
} }
throw error; throw error;
}); })
.catch(err => this.handleError(err));
} }
// Hopefully we can get rid of this. It's only used for config and hooks. // Hopefully we can get rid of this. It's only used for config and hooks.
@@ -436,7 +462,8 @@ export class MongoStorageAdapter implements StorageAdapter {
const mongoUpdate = transformUpdate(className, update, schema); const mongoUpdate = transformUpdate(className, update, schema);
const mongoWhere = transformWhere(className, query, schema); const mongoWhere = transformWhere(className, query, schema);
return this._adaptiveCollection(className) return this._adaptiveCollection(className)
.then(collection => collection.upsertOne(mongoWhere, mongoUpdate)); .then(collection => collection.upsertOne(mongoWhere, mongoUpdate))
.catch(err => this.handleError(err));
} }
// Executes a find. Accepts: className, query in Parse format, and { skip, limit, sort }. // Executes a find. Accepts: className, query in Parse format, and { skip, limit, sort }.
@@ -461,6 +488,7 @@ export class MongoStorageAdapter implements StorageAdapter {
readPreference, readPreference,
})) }))
.then(objects => objects.map(object => mongoObjectToParseObject(className, object, schema))) .then(objects => objects.map(object => mongoObjectToParseObject(className, object, schema)))
.catch(err => this.handleError(err));
} }
// Create a unique index. Unique indexes on nullable fields are not allowed. Since we don't // Create a unique index. Unique indexes on nullable fields are not allowed. Since we don't
@@ -482,14 +510,15 @@ export class MongoStorageAdapter implements StorageAdapter {
throw new Parse.Error(Parse.Error.DUPLICATE_VALUE, 'Tried to ensure field uniqueness for a class that already has duplicates.'); throw new Parse.Error(Parse.Error.DUPLICATE_VALUE, 'Tried to ensure field uniqueness for a class that already has duplicates.');
} }
throw error; throw error;
}); })
.catch(err => this.handleError(err));
} }
// Used in tests // Used in tests
_rawFind(className: string, query: QueryType) { _rawFind(className: string, query: QueryType) {
return this._adaptiveCollection(className).then(collection => collection.find(query, { return this._adaptiveCollection(className).then(collection => collection.find(query, {
maxTimeMS: this._maxTimeMS, maxTimeMS: this._maxTimeMS,
})); })).catch(err => this.handleError(err));
} }
// Executes a count. // Executes a count.
@@ -500,7 +529,8 @@ export class MongoStorageAdapter implements StorageAdapter {
.then(collection => collection.count(transformWhere(className, query, schema), { .then(collection => collection.count(transformWhere(className, query, schema), {
maxTimeMS: this._maxTimeMS, maxTimeMS: this._maxTimeMS,
readPreference, readPreference,
})); }))
.catch(err => this.handleError(err));
} }
distinct(className: string, schema: SchemaType, query: QueryType, fieldName: string) { distinct(className: string, schema: SchemaType, query: QueryType, fieldName: string) {
@@ -520,7 +550,8 @@ export class MongoStorageAdapter implements StorageAdapter {
} }
return mongoObjectToParseObject(className, object, schema); return mongoObjectToParseObject(className, object, schema);
}); });
}); })
.catch(err => this.handleError(err));
} }
aggregate(className: string, schema: any, pipeline: any, readPreference: ?string) { aggregate(className: string, schema: any, pipeline: any, readPreference: ?string) {
@@ -573,7 +604,8 @@ export class MongoStorageAdapter implements StorageAdapter {
}); });
return results; return results;
}) })
.then(objects => objects.map(object => mongoObjectToParseObject(className, object, schema))); .then(objects => objects.map(object => mongoObjectToParseObject(className, object, schema)))
.catch(err => this.handleError(err));
} }
_parseReadPreference(readPreference: ?string): ?string { _parseReadPreference(readPreference: ?string): ?string {
@@ -609,12 +641,14 @@ export class MongoStorageAdapter implements StorageAdapter {
createIndex(className: string, index: any) { createIndex(className: string, index: any) {
return this._adaptiveCollection(className) return this._adaptiveCollection(className)
.then(collection => collection._mongoCollection.createIndex(index)); .then(collection => collection._mongoCollection.createIndex(index))
.catch(err => this.handleError(err));
} }
createIndexes(className: string, indexes: any) { createIndexes(className: string, indexes: any) {
return this._adaptiveCollection(className) return this._adaptiveCollection(className)
.then(collection => collection._mongoCollection.createIndexes(indexes)); .then(collection => collection._mongoCollection.createIndexes(indexes))
.catch(err => this.handleError(err));
} }
createIndexesIfNeeded(className: string, fieldName: string, type: any) { createIndexesIfNeeded(className: string, fieldName: string, type: any) {
@@ -656,17 +690,20 @@ export class MongoStorageAdapter implements StorageAdapter {
getIndexes(className: string) { getIndexes(className: string) {
return this._adaptiveCollection(className) return this._adaptiveCollection(className)
.then(collection => collection._mongoCollection.indexes()); .then(collection => collection._mongoCollection.indexes())
.catch(err => this.handleError(err));
} }
dropIndex(className: string, index: any) { dropIndex(className: string, index: any) {
return this._adaptiveCollection(className) return this._adaptiveCollection(className)
.then(collection => collection._mongoCollection.dropIndex(index)); .then(collection => collection._mongoCollection.dropIndex(index))
.catch(err => this.handleError(err));
} }
dropAllIndexes(className: string) { dropAllIndexes(className: string) {
return this._adaptiveCollection(className) return this._adaptiveCollection(className)
.then(collection => collection._mongoCollection.dropIndexes()); .then(collection => collection._mongoCollection.dropIndexes())
.catch(err => this.handleError(err));
} }
updateSchemaWithIndexes(): Promise<any> { updateSchemaWithIndexes(): Promise<any> {
@@ -676,7 +713,8 @@ export class MongoStorageAdapter implements StorageAdapter {
return this.setIndexesFromMongo(schema.className); return this.setIndexesFromMongo(schema.className);
}); });
return Promise.all(promises); return Promise.all(promises);
}); })
.catch(err => this.handleError(err));
} }
} }