diff --git a/README b/README index 72c03dc..e1693d9 100644 --- a/README +++ b/README @@ -78,6 +78,16 @@ OPTIONS (database 'test', collection 'customer_reviews'); -- collect data distribution statistics ANALYZE customer_reviews; +If you are using MongoDB authentication, you will need to indicate that when creating the server: + +CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw +OPTIONS (address '127.0.0.1', port '27017', use_auth 'true') + +Then you must add a user mapping for any PostgreSQL user: + +CREATE USER MAPPING FOR (username | CURRENT_USER | PUBLIC) +SERVER mongo_server OPTIONS (username 'mongo_username', password 'mongo_password'); + Limitations ----------- @@ -95,7 +105,7 @@ Limitations Copyright --------- -Copyright (c) 2012 Citus Data, Inc. +Copyright (c) 2013 Citus Data, Inc. This module is free software; you can redistribute it and/or modify it under the GNU GPL v3.0 License. diff --git a/mongo_fdw.c b/mongo_fdw.c index a22aa56..8860808 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -56,7 +56,7 @@ static Const * SerializeDocument(bson *document); static bson * DeserializeDocument(Const *constant); static double ForeignTableDocumentCount(Oid foreignTableId); static MongoFdwOptions * MongoGetOptions(Oid foreignTableId); -static char * MongoGetOptionValue(Oid foreignTableId, const char *optionName); +static char * MongoGetOptionValue(List *optionList, const char *optionName); static HTAB * ColumnMappingHash(Oid foreignTableId, List *columnList); static void FillTupleSlot(const bson *bsonDocument, const char *bsonDocumentKey, HTAB *columnMappingHash, Datum *columnValues, @@ -296,7 +296,7 @@ MongoGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, Oid foreignTableId) NULL, /* no outer rel either */ NIL); /* no fdw_private data */ - /* add foreign path as the only possible path */ + /* add foreign path as the only possible path */ add_path(baserel, foreignPath); } @@ -389,11 +389,16 @@ MongoBeginForeignScan(ForeignScanState *scanState, int executorFlags) mongo *mongoConnection = NULL; mongo_cursor *mongoCursor = NULL; int32 connectStatus = MONGO_ERROR; + int32 authStatus = MONGO_ERROR; Oid foreignTableId = InvalidOid; List *columnList = NIL; HTAB *columnMappingHash = NULL; char *addressName = NULL; int32 portNumber = 0; + bool useAuth = false; + char *username = NULL; + char *password = NULL; + char *databaseName = NULL; int32 errorCode = 0; StringInfo namespaceName = NULL; ForeignScan *foreignScan = NULL; @@ -431,6 +436,27 @@ MongoBeginForeignScan(ForeignScanState *scanState, int executorFlags) errhint("Mongo driver connection error: %d", errorCode))); } + useAuth = mongoFdwOptions->useAuth; + if (useAuth) + { + username = mongoFdwOptions->username; + password = mongoFdwOptions->password; + databaseName = mongoFdwOptions->databaseName; + + authStatus = mongo_cmd_authenticate( + mongoConnection, databaseName, username, password); + + if (authStatus != MONGO_OK) + { + mongo_destroy(mongoConnection); + mongo_dispose(mongoConnection); + + ereport(ERROR, (errmsg("could not authenticate with user %s on database %s", + username, databaseName), + errhint("Update user mapping for user."))); + } + } + /* deserialize query document; and create column info hash */ foreignScan = (ForeignScan *) scanState->ss.ps.plan; foreignPrivateList = foreignScan->fdw_private; @@ -449,6 +475,7 @@ MongoBeginForeignScan(ForeignScanState *scanState, int executorFlags) /* create cursor for collection name and set query */ mongoCursor = mongo_cursor_create(); mongo_cursor_init(mongoCursor, mongoConnection, namespaceName->data); + mongo_cursor_set_options(mongoCursor, MONGO_SLAVE_OK); mongo_cursor_set_query(mongoCursor, queryDocument); /* create and set foreign execution state */ @@ -639,7 +666,8 @@ ForeignTableDocumentCount(Oid foreignTableId) MongoFdwOptions *options = NULL; mongo *mongoConnection = NULL; const bson *emptyQuery = NULL; - int32 status = MONGO_ERROR; + int32 connectStatus = MONGO_ERROR; + int32 authStatus = MONGO_OK; double documentCount = 0.0; /* resolve foreign table options; and connect to mongo server */ @@ -648,8 +676,16 @@ ForeignTableDocumentCount(Oid foreignTableId) mongoConnection = mongo_create(); mongo_init(mongoConnection); - status = mongo_connect(mongoConnection, options->addressName, options->portNumber); - if (status == MONGO_OK) + connectStatus = mongo_connect(mongoConnection, options->addressName, + options->portNumber); + if (connectStatus == MONGO_OK && options->useAuth) + { + authStatus = mongo_cmd_authenticate( + mongoConnection, options->databaseName, options->username, + options->password); + } + + if (connectStatus == MONGO_OK && authStatus == MONGO_OK) { documentCount = mongo_count(mongoConnection, options->databaseName, options->collectionName, emptyQuery); @@ -678,16 +714,30 @@ MongoGetOptions(Oid foreignTableId) char *addressName = NULL; char *portName = NULL; int32 portNumber = 0; + char *useAuthStr = NULL; + bool useAuth = false; char *databaseName = NULL; char *collectionName = NULL; + char *username = NULL; + char *password = NULL; + ForeignTable *foreignTable = NULL; + ForeignServer *foreignServer = NULL; + UserMapping *userMapping = NULL; + List *optionList = NIL; - addressName = MongoGetOptionValue(foreignTableId, OPTION_NAME_ADDRESS); + foreignTable = GetForeignTable(foreignTableId); + foreignServer = GetForeignServer(foreignTable->serverid); + + optionList = list_concat(optionList, foreignTable->options); + optionList = list_concat(optionList, foreignServer->options); + + addressName = MongoGetOptionValue(optionList, OPTION_NAME_ADDRESS); if (addressName == NULL) { addressName = pstrdup(DEFAULT_IP_ADDRESS); } - portName = MongoGetOptionValue(foreignTableId, OPTION_NAME_PORT); + portName = MongoGetOptionValue(optionList, OPTION_NAME_PORT); if (portName == NULL) { portNumber = DEFAULT_PORT_NUMBER; @@ -697,23 +747,43 @@ MongoGetOptions(Oid foreignTableId) portNumber = pg_atoi(portName, sizeof(int32), 0); } - databaseName = MongoGetOptionValue(foreignTableId, OPTION_NAME_DATABASE); + databaseName = MongoGetOptionValue(optionList, OPTION_NAME_DATABASE); if (databaseName == NULL) { databaseName = pstrdup(DEFAULT_DATABASE_NAME); } - collectionName = MongoGetOptionValue(foreignTableId, OPTION_NAME_COLLECTION); + collectionName = MongoGetOptionValue(optionList, OPTION_NAME_COLLECTION); if (collectionName == NULL) { collectionName = get_rel_name(foreignTableId); } + useAuthStr = MongoGetOptionValue(optionList, OPTION_NAME_USE_AUTH); + if (useAuthStr != NULL) + { + if(!parse_bool(useAuthStr, &useAuth)) + { + useAuth = false; + } + } + + if(useAuth) + { + userMapping = GetUserMapping(GetUserId(), foreignTable->serverid); + optionList = list_concat(optionList, userMapping->options); + username = MongoGetOptionValue(optionList, OPTION_NAME_USERNAME); + password = MongoGetOptionValue(optionList, OPTION_NAME_PASSWORD); + } + mongoFdwOptions = (MongoFdwOptions *) palloc0(sizeof(MongoFdwOptions)); mongoFdwOptions->addressName = addressName; mongoFdwOptions->portNumber = portNumber; + mongoFdwOptions->useAuth = useAuth; mongoFdwOptions->databaseName = databaseName; mongoFdwOptions->collectionName = collectionName; + mongoFdwOptions->username = username; + mongoFdwOptions->password = password; return mongoFdwOptions; } @@ -725,20 +795,11 @@ MongoGetOptions(Oid foreignTableId) * option's value. */ static char * -MongoGetOptionValue(Oid foreignTableId, const char *optionName) +MongoGetOptionValue(List *optionList, const char *optionName) { - ForeignTable *foreignTable = NULL; - ForeignServer *foreignServer = NULL; - List *optionList = NIL; ListCell *optionCell = NULL; char *optionValue = NULL; - foreignTable = GetForeignTable(foreignTableId); - foreignServer = GetForeignServer(foreignTable->serverid); - - optionList = list_concat(optionList, foreignTable->options); - optionList = list_concat(optionList, foreignServer->options); - foreach(optionCell, optionList) { DefElem *optionDef = (DefElem *) lfirst(optionCell); @@ -954,7 +1015,7 @@ ColumnTypesCompatible(bson_type bsonType, Oid columnTypeId) } break; } - case NAMEOID: + case NAMEOID: { /* * We currently overload the NAMEOID type to represent the BSON @@ -1132,7 +1193,7 @@ ColumnValue(bson_iterator *bsonIterator, Oid columnTypeId, int32 columnTypeMod) columnValue = CStringGetTextDatum(value); break; } - case NAMEOID: + case NAMEOID: { char value[NAMEDATALEN]; Datum valueDatum = 0; @@ -1394,7 +1455,7 @@ MongoAcquireSampleRows(Relation relation, int errorLevel, } break; - } + } /* * The first targetRowCount sample rows are simply copied into the diff --git a/mongo_fdw.h b/mongo_fdw.h index ea0e3ef..a1fb981 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -20,6 +20,7 @@ #include "fmgr.h" #include "catalog/pg_foreign_server.h" #include "catalog/pg_foreign_table.h" +#include "catalog/pg_user_mapping.h" #include "utils/datetime.h" #include "nodes/pg_list.h" #include "nodes/relation.h" @@ -29,8 +30,11 @@ /* Defines for valid option names */ #define OPTION_NAME_ADDRESS "address" #define OPTION_NAME_PORT "port" +#define OPTION_NAME_USE_AUTH "use_auth" #define OPTION_NAME_DATABASE "database" #define OPTION_NAME_COLLECTION "collection" +#define OPTION_NAME_USERNAME "username" +#define OPTION_NAME_PASSWORD "password" /* Default values for option parameters */ #define DEFAULT_IP_ADDRESS "127.0.0.1" @@ -60,16 +64,21 @@ typedef struct MongoValidOption /* Array of options that are valid for mongo_fdw */ -static const uint32 ValidOptionCount = 4; +static const uint32 ValidOptionCount = 7; static const MongoValidOption ValidOptionArray[] = { /* foreign server options */ { OPTION_NAME_ADDRESS, ForeignServerRelationId }, { OPTION_NAME_PORT, ForeignServerRelationId }, + { OPTION_NAME_USE_AUTH, ForeignServerRelationId }, /* foreign table options */ { OPTION_NAME_DATABASE, ForeignTableRelationId }, - { OPTION_NAME_COLLECTION, ForeignTableRelationId } + { OPTION_NAME_COLLECTION, ForeignTableRelationId }, + + /* user mapping options */ + { OPTION_NAME_USERNAME, UserMappingRelationId }, + { OPTION_NAME_PASSWORD, UserMappingRelationId } }; @@ -82,8 +91,11 @@ typedef struct MongoFdwOptions { char *addressName; int32 portNumber; + bool useAuth; char *databaseName; char *collectionName; + char *username; + char *password; } MongoFdwOptions;