From 3e5e785b7f5403fff021686502663978719f606b Mon Sep 17 00:00:00 2001 From: David Crawford Date: Thu, 18 Apr 2013 11:11:18 -0700 Subject: [PATCH 1/3] Add authentication support. --- README | 12 ++++++- mongo_fdw.c | 97 +++++++++++++++++++++++++++++++++++++++++++---------- mongo_fdw.h | 16 +++++++-- 3 files changed, 104 insertions(+), 21 deletions(-) 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..4bc9279 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, @@ -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); diff --git a/mongo_fdw.h b/mongo_fdw.h index ea0e3ef..1cccc42 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; From ae02c543d723bbb53d07081d31d961f6c56f2324 Mon Sep 17 00:00:00 2001 From: David Crawford Date: Thu, 18 Apr 2013 11:15:53 -0700 Subject: [PATCH 2/3] Fix whitespace. --- mongo_fdw.c | 130 ++++++++++++++++++++++++++-------------------------- mongo_fdw.h | 14 +++--- 2 files changed, 72 insertions(+), 72 deletions(-) diff --git a/mongo_fdw.c b/mongo_fdw.c index 4bc9279..8860808 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -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,16 +389,16 @@ MongoBeginForeignScan(ForeignScanState *scanState, int executorFlags) mongo *mongoConnection = NULL; mongo_cursor *mongoCursor = NULL; int32 connectStatus = MONGO_ERROR; - int32 authStatus = 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; + bool useAuth = false; + char *username = NULL; + char *password = NULL; + char *databaseName = NULL; int32 errorCode = 0; StringInfo namespaceName = NULL; ForeignScan *foreignScan = NULL; @@ -436,26 +436,26 @@ 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; + useAuth = mongoFdwOptions->useAuth; + if (useAuth) + { + username = mongoFdwOptions->username; + password = mongoFdwOptions->password; + databaseName = mongoFdwOptions->databaseName; - authStatus = mongo_cmd_authenticate( - mongoConnection, databaseName, username, password); + authStatus = mongo_cmd_authenticate( + mongoConnection, databaseName, username, password); - if (authStatus != MONGO_OK) - { - mongo_destroy(mongoConnection); - mongo_dispose(mongoConnection); + 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."))); - } - } + 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; @@ -667,7 +667,7 @@ ForeignTableDocumentCount(Oid foreignTableId) mongo *mongoConnection = NULL; const bson *emptyQuery = NULL; int32 connectStatus = MONGO_ERROR; - int32 authStatus = MONGO_OK; + int32 authStatus = MONGO_OK; double documentCount = 0.0; /* resolve foreign table options; and connect to mongo server */ @@ -677,15 +677,15 @@ ForeignTableDocumentCount(Oid foreignTableId) mongo_init(mongoConnection); connectStatus = mongo_connect(mongoConnection, options->addressName, - options->portNumber); + options->portNumber); if (connectStatus == MONGO_OK && options->useAuth) - { - authStatus = mongo_cmd_authenticate( - mongoConnection, options->databaseName, options->username, - options->password); - } + { + authStatus = mongo_cmd_authenticate( + mongoConnection, options->databaseName, options->username, + options->password); + } - if (connectStatus == MONGO_OK && authStatus == MONGO_OK) + if (connectStatus == MONGO_OK && authStatus == MONGO_OK) { documentCount = mongo_count(mongoConnection, options->databaseName, options->collectionName, emptyQuery); @@ -714,22 +714,22 @@ MongoGetOptions(Oid foreignTableId) char *addressName = NULL; char *portName = NULL; int32 portNumber = 0; - char *useAuthStr = NULL; - bool useAuth = false; + 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; + char *username = NULL; + char *password = NULL; + ForeignTable *foreignTable = NULL; + ForeignServer *foreignServer = NULL; + UserMapping *userMapping = NULL; + List *optionList = NIL; - foreignTable = GetForeignTable(foreignTableId); - foreignServer = GetForeignServer(foreignTable->serverid); + foreignTable = GetForeignTable(foreignTableId); + foreignServer = GetForeignServer(foreignTable->serverid); - optionList = list_concat(optionList, foreignTable->options); - optionList = list_concat(optionList, foreignServer->options); + optionList = list_concat(optionList, foreignTable->options); + optionList = list_concat(optionList, foreignServer->options); addressName = MongoGetOptionValue(optionList, OPTION_NAME_ADDRESS); if (addressName == NULL) @@ -759,31 +759,31 @@ MongoGetOptions(Oid foreignTableId) 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); - } + 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->useAuth = useAuth; mongoFdwOptions->databaseName = databaseName; mongoFdwOptions->collectionName = collectionName; - mongoFdwOptions->username = username; - mongoFdwOptions->password = password; + mongoFdwOptions->username = username; + mongoFdwOptions->password = password; return mongoFdwOptions; } @@ -1015,7 +1015,7 @@ ColumnTypesCompatible(bson_type bsonType, Oid columnTypeId) } break; } - case NAMEOID: + case NAMEOID: { /* * We currently overload the NAMEOID type to represent the BSON @@ -1193,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; @@ -1455,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 1cccc42..19cda99 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -70,15 +70,15 @@ static const MongoValidOption ValidOptionArray[] = /* foreign server options */ { OPTION_NAME_ADDRESS, ForeignServerRelationId }, { OPTION_NAME_PORT, ForeignServerRelationId }, - { OPTION_NAME_USE_AUTH, ForeignServerRelationId }, + { OPTION_NAME_USE_AUTH, ForeignServerRelationId }, /* foreign table options */ { OPTION_NAME_DATABASE, ForeignTableRelationId }, { OPTION_NAME_COLLECTION, ForeignTableRelationId }, - /* user mapping options */ - { OPTION_NAME_USERNAME, UserMappingRelationId }, - { OPTION_NAME_PASSWORD, UserMappingRelationId }, + /* user mapping options */ + { OPTION_NAME_USERNAME, UserMappingRelationId }, + { OPTION_NAME_PASSWORD, UserMappingRelationId }, }; @@ -91,11 +91,11 @@ typedef struct MongoFdwOptions { char *addressName; int32 portNumber; - bool useAuth; + bool useAuth; char *databaseName; char *collectionName; - char *username; - char *password; + char *username; + char *password; } MongoFdwOptions; From ffb3c654b12c4245601d839ffa945da5abc98b28 Mon Sep 17 00:00:00 2001 From: David Crawford Date: Sat, 20 Apr 2013 13:34:50 -0700 Subject: [PATCH 3/3] Remove stray comma. --- mongo_fdw.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mongo_fdw.h b/mongo_fdw.h index 19cda99..a1fb981 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -78,7 +78,7 @@ static const MongoValidOption ValidOptionArray[] = /* user mapping options */ { OPTION_NAME_USERNAME, UserMappingRelationId }, - { OPTION_NAME_PASSWORD, UserMappingRelationId }, + { OPTION_NAME_PASSWORD, UserMappingRelationId } };