Mongo authentication succeeds with command line or interpreter, fails with libraries - mongodb

For a given user foo with password bar, I can successfully authenticate against the admin database and get the desired result from one of our shard nodes with the following command:
mongo PRIMARY --host exampleShard1.com \
--port 27018 \
--username foo \
-p bar \
--authenticationDatabase 'admin' \
--eval 'db.serverStatus().connections'
However, when attempting to do the exact same command via a third party library such as the Java's MongoDB Driver or Python's pymongo, the authentication passes successfully but I'm unable to execute any commands successfully due to "not authorized" errors.
For example, here's a pymongo version of the working command above:
#!/usr/bin/python3
from pymongo import MongoClient
mongoClient = MongoClient("exampleShard1.com", 27018)
mongoClient.admin.authenticate("foo", "bar")
primaryDb = mongoClient["PRIMARY"]
print(primaryDb.eval("db.serverStatus().connections"))
Despite this appearing to be equivalent to the origin working command, this version fails with a version of the following error:
not authorized on local to execute command
{
$eval: db.serverStatus().connections,
args: [],
lsid: {
id: UUID("f5a936ee-71ad-4568-8bf2-a45c69424200")
},
$clusterTime: {
signature: {
keyId: 111,
hash: BinData(0, 6F)
},
clusterTime: Timestamp(1536848021, 159)
},
$db: "local",
$readPreference: {
mode: "primaryPreferred"
}
}
This is despite the authenticate() method returning "True", proving a successful authentication.
My guess would be there's a required value being set automatically from the command line that isn't being set from the script version, or that the command line version is somehow bypassing an extra level of authentication that the script version is caught in.
At this point, I'm unfortunately out of things to try, and I've exhausted Google for help. Any guidance would be greatly appreciated!

Related

Mongosh insert one modified document in MongoDB

I'm trying to insert one registry in mongodb with mongosh and ubuntu bash. I've get one registry with mongosh . I have to edit 3 fields and make an insert. I thought to make the edition with jq but I don't get it done.
{ "_id": {"fileName": "xxxxxx","namespace": "yyyyyy" },
"metainfo": {"file-type": "csv","environment": "int",
"creation-date": 1672306975130000000,"file-name":"xxxxxxx" }
}
I've to edit creation-date (is the date en nanos), the enviroment, change part of the fileName (make a substring). I've get the document with --eval "EJSON.stringlify(....)"
the command with jq I've tried is:
newDocument=$(echo "$fileData" | jq '.metainfo.environment |= "pro"')
and gives me error:
parse error: Invalid numeric literal at line 1, column 8
I've validated the JSON and it's well formed.
After made the changes I've to make Then make the insert. I've thought made with
--eval "......insertOne(EJSON.stringlify($newDocument))"
is this correct? What would be the best mannerto do all this?
Thanks for all.
The error was giving me because I was making the request without --quiet parameter.
The mongo shell allows json without problems.

Passing Command to Mongo Shell with --eval returns undefined

I'm attempting to write a script to deploy mongodb for customers using Powershell (whatever version comes with Server2016). I can install as a service and get it up and running just fine, but I want to also add authentication. Specifically I want to add an admin user and the database user.
Initially I create the mongod service with this powershell function:
function createMongoService([string] $mongoDbConfigPath, [string] $serviceName, [string] $storePath, [string] $authCmd)
{
$mongodexe = "$mongoInstallPath\bin\mongod.exe"
Write-Host "Activating Mongod as a service '$serviceName'"
"auth cmd = $authCmd"
& $mongodexe --config "$mongoDbConfigPath" --install --serviceName "$serviceName" --serviceDisplayName "Storfirst MongoDB" --port 27710 --dbpath "$storePath\db" $authCmd
if ($LASTEXITCODE -ne 0)
{
throw "Failed to start service"
}
}
In the initial case, $authCmd is "". Looking at the logs, I can confirm that the service is being started WITHOUT authentication.
2018-06-13T16:00:26.600-0700 I CONTROL [initandlisten] ** WARNING:
Access control is not enabled for the database.
2018-06-13T16:00:26.600-0700 I CONTROL [initandlisten] **
Read and write access to data and configuration is unrestricted.
Then I go to create the mongo user with this function:
function createAdminUser( [string] $mongoInstallPath, [string] $mongoAdminName, [string] $mongoAdminPass)
{
Write-Host "Creating admin user"
$createAdminCmd = `
"db.createUser( `
{ `
user: `"$mongoAdminName`",
pwd: `"$mongoAdminPass`",
roles: [ { role: `"userAdminAnyDatabase`", db: `"admin`" } ] `
} `
);"
"$createAdminCmd"
& "$mongoInstallPath\bin\mongo.exe" admin --port 27710 --eval "$createAdminCmd"
if ($LASTEXITCODE -ne 0)
{
throw "Failed to create admin user"
}
}
But this fails with the following error printed to screen (nothing about it is logged in the mongodb log). mongoAdminName is equal to administrator in this run, but it gives me the same results with the equivalent admin name if I use something else.
MongoDB shell version v3.4.15-49-g4ef027f98d connecting to:
mongodb://127.0.0.1:27710/admin MongoDB server version:
3.4.15-49-g4ef027f98d 2018-06-13T16:10:00.125-0700 E QUERY [thread1] ReferenceError: administrator is not defined : #(shell
eval):1:17
This happens even if I enter the expected command by hand in powershell.
I use a similar command to create users on our custom mongodb docker container and have no problems with that, but I need a non-container solution in this case.
The plan is, once I've created the admin user, reexecute the createMongoService, this time with authCmd equal to "--auth", then use the admin user to create the database user with standard authentication practices.
My questions are "whats going on here?!?" and "how do I fix this?"
The error administrator is not defined actually refers to an unquoted value present in the produced output. Basically that's the user argument and if you change it to "bill" then you get bill is not defined. So it's not a "MongoDB error" but a JavaScript error of the interpreter.
This is because of the interpolation of double quotes inside double quotes, so the solution is to change the quote scheme instead:
$createAdminCmd =
"db.createUser(
{
user: '" + $mongoAdminName + "',
pwd: '" + $mongoAdminPass + "',
roles: [ { role: 'userAdminAnyDatabase', db: 'admin' } ]
}
);"
The alternate is to also escape the double quotes in a way that the JavaScript interpreter is happy with:
$createAdminCmd =
"db.createUser(
{
user: \`"$mongoAdminName\`",
pwd: \`"$mongoAdminPass\`",
roles: [ { role: \`"userAdminAnyDatabase\`", db: \`"admin\`" } ]
}
);"
Then the rest of it works just fine.
Remember that even though it "looks like" what you think you might just be typing in, from the perspective of the JavaScript interpreter which needs to "eval" the content before execution it's still "just a string". Therefore the same rules apply as they always do for JavaScript to interpolate a "string".

How to set rs.slaveOk() in secondary mongodb servers in replicaset via commandline?

How to set rs.slaveOk() in secondary mongodb servers in replicaset via commandline?
I tried following methods :
${MONGO_HOME}/bin/mongo --port ${MONGO_PORT2} --host ${MONGO_SECONDARY2} --eval "printjson(rs.slaveOk())"
${MONGO_HOME}/bin/mongo --port ${MONGO_PORT2} --host ${MONGO_SECONDARY2} --eval "printjson(rs.slaveOk(true))"
${MONGO_HOME}/bin/mongo --port ${MONGO_PORT2} --host ${MONGO_SECONDARY2} --eval "printjson(db.getSiblingDB('admin').getMongo().setSlaveOk())"
the command executes with undefined in the output log.
I am trying to set this via the shell in primary server.
Create a file /etc/mongorc.js and add rs.slaveOk() there. The file is being evaluated on each shell startup.
For more information have a look here
From MongoDB version 4.4 onwards, you might get a warning displayed like:
WARNING: slaveOk() is deprecated and may be removed in the next major release. Please use secondaryOk() instead.
So, please prefer using rs.secondaryOk()
Calling the below should work fine, there is no return type for the method so nothing will get printed back to the screen
${MONGO_HOME}/bin/mongo --port ${MONGO_PORT2} --host ${MONGO_SECONDARY2} --eval "rs.slaveOk()"
Running rs.slaveOk in the mongo.exe will also how how it is implemented as it is just a helper method:
> rs.slaveOk
function (value) { return db.getMongo().setSlaveOk(value); }
>
And also the setSlaveOk method:
> db.getMongo().setSlaveOk
function ( value ) {
if( value == undefined ) value = true;
this.slaveOk = value;
}
You could always try to query one of the collections on the secondary to make sure the node is queryable:
> db.test.findOne()
null
Update - bit more clarity
Setting slaveOk() is only valid for that console session that it was executed in, so you would need to pass in a script or stay connected to the console with the --shell arguments for exmaple
C:\mongodb\bin>mongo.exe --port 27012 --eval "rs.slaveOk()" --shell
MongoDB shell version: 3.0.5
connecting to: 127.0.0.1:27012/test
type "help" for help
rs1:SECONDARY> db.test.find()
{ "_id" : ObjectId("5630fdf2af4abd9f8ae7f79c"), "test" : true }
rs1:SECONDARY>
If we don't pass in the rs.slaveOk() the we get the following response:
C:\mongodb\bin>mongo.exe --port 27012 --shell
MongoDB shell version: 3.0.5
connecting to: 127.0.0.1:27012/test
type "help" for help
rs1:SECONDARY> db.test.find()
Error: error: { "$err" : "not master and slaveOk=false", "code" : 13435 }
rs1:SECONDARY> exit
bye
JFYI :
looks like rs.slaveOk() will be deprecated soon, instead MongoDB suggest to use rs.secondaryOk()
Following is the official warning you gonna see in MongoShell:
WARNING: slaveOk() is deprecated and may be removed in the next major
release. Please use secondaryOk() instead.
Cheers

How to execute the mongo cloneCollection command to copy a collection to a remote server

I'd like to copy a collection from one database to an instance on another server. From other stackoverflow questions, I understand the correct way to do that is with this command:
{ cloneCollection: "<collection>", from: "<hostname>", query: { <query> } }
via http://docs.mongodb.org/manual/reference/command/cloneCollection/
However, I don't understand where do I enter this command? It isn't accepted as...
$ mongod { cloneCollection: "remote", from: "ec2-whatever-amazon.com"}
How do I copy a remote collection at db.remote.collname to db.local.collname using this cloneCollection syntax via command line?
MongoDB database commands are run using db.runCommand() from the mongo shell. Refer to http://docs.mongodb.org/manual/tutorial/use-database-commands/.
Try something like this (using another database command for simplicity):
$ mongo
> db.runCommand({ isMaster: 1})
{
"ismaster" : true,
"maxBsonObjectSize" : 16777216,
"localTime" : ISODate("2014-02-18T22:30:04.417Z"),
"ok" : 1
}
>

mongo --shell file.js and "use" statement

can't find solution for simple question:
I have file text.js
use somedb
db.somecollection.findOne()
When I run this file in cmd with redirection command from file:
"mongo < text.js"
it's work properly
But when I try this way
"mongo text.js" or "mongo --shell test.js"
I got this error message
MongoDB shell version: 2.2.0
connecting to: test
type "help" for help
Wed Dec 05 16:05:21 SyntaxError: missing ; before statement pathToFile\test.js.js:1
failed to load: pathToFile\test.js.js
It's fail on "use somedb". If I remove this line, it's run without error, but console is clear.
is there any idea, what is this and how to fix?
I'm tying to find sollution for this, to create build tool for Sublime Text 2.
default build file was
{
"cmd": ["mongo","$file"]
}
but in this case I get the error above
PS. right after posting this question I find sollution for SublimeText2:
{
"selector": "source.js",
"shell":true,
"cmd": ["mongo < ${file}"]
}
PSS. right after posting this question I find sollution for SublimeText3:
{
"selector": "source.js",
"shell":true,
"cmd": ["mongo","<", "$file"]
}
this build tool work properly
use dbname is a helper function in the interactive shell which does not work when you are using mongo shell with a JS script file like you are.
There are multiple solutions to this. The best one, IMO is to explicitly pass the DB name along with host and port name to mongo like this:
mongo hostname:27017/dbname mongoscript.js // replace 27017 with your port number
A better way to do this would be to define the DB at the beginning of your script:
mydb=db.getSiblingDB("yourdbname");
mydb.collection.findOne();
etc.
The latter is preferable as it allows you to interact with multiple DBs in the same script if you need to do so.
You can specify the database while starting the mongo client:
mongo somedb text.js
To get the output from the client to stdout just use the printjson function in your script:
printjson(db.somecollection.findOne());
Mongo needs to be invoked from a shell to get that mode, with Ansible you would have this:
- name: mongo using different databases
action: shell /usr/bin/mongo < text.js
Instead of this:
- name: mongo breaking
command: /usr/bin/mongo < text.js
This is what finally worked for me on Windows + Sublime Text 2 + MongoDB 2.6.5
{
"selector": "source.js",
"shell":true,
"cmd": ["mongo","<", "$file"],
"working_dir" : "C:\\MongoDB\\bin"
}