twisted / txmongo

asynchronous python driver for mongo
https://txmongo.readthedocs.io
Apache License 2.0
338 stars 102 forks source link

txmongo.collection.Collection#rename() without admin DB #204

Open cpg1111 opened 7 years ago

cpg1111 commented 7 years ago

Hi there, I was trying to use txmongo.collection.Collection#rename() with a user without admin permissions. This can be done in Mongo just fine without admin permissions, as you can do db.collection.renameCollection() within a db you have rw permissions from in the mongo repl. Looking at the source for txmongo.collection.Collection#rename(), it does an admin command by default. Can this be switched to use the non-admin one by default and perhaps a flag for the admin version or a different method?

IlyaSkriblovsky commented 7 years ago

Hi,

I've just checked if txmongo's rename works for a user with single role "roles" : [ { "role" : "readWrite", "db" : "mydb" } ] and it does well.

According to the docs, renameCollection command should always be issued against admin database. Nevertheless it should work as long as you have readWrite role for a given DB (and if you are renaming collection inside this db, not moving it to another one).

If you look into the source code of REPL's renameCollection you can see that it issues the command against admin DB too:

> db.coll.renameCollection
function (newName, dropTarget) {
    if (arguments.length === 1 && typeof newName === 'object') {
        if (newName.hasOwnProperty('dropTarget')) {
            dropTarget = newName['dropTarget'];
        }
        newName = newName['to'];
    }
    if (typeof dropTarget === 'undefined') {
        dropTarget = false;
    }
    if (typeof newName !== 'string' || typeof dropTarget !== 'boolean') {
        throw Error(
            'renameCollection must either take a string and an optional boolean or an object.');
    }
    return this._db._adminCommand({
        renameCollection: this._fullName,
        to: this._db._name + "." + newName,
        dropTarget: dropTarget
    });
}
> db.adminCommand
function (obj, extra) {
        if (this._name == "admin")
            return this.runCommand(obj, extra);
        return this.getSiblingDB("admin").runCommand(obj, extra);
    }

Please check what roles does your DB user have and correct me if I'm wrong.

cpg1111 commented 7 years ago

So I am using a user with dbOwner permissions on a specific DB, but no role for the admin and I am geting a permissions issue. mongo has two renameCollection's the one you linked and this one. In the latter, it is exec'd on the selected DB, not requiring rw to admin.

IlyaSkriblovsky commented 7 years ago

Sorry, but I can't reproduce the issue.

I tried as follows:

  1. Started fresh clean MongoDB 3.4 instance (docker run --rm -it -p 27017:27017 mongo --auth)
  2. Ran mongo admin and did: db.createUser({user: 'userAdmin', pwd: 'qwe', roles:[{role: 'userAdminAnyDatabase', db: 'admin'}]})
  3. Ran mongo -u userAdmin -p qwe admin and did: db.createUser({user: 'test', pwd: 'test', roles:[{role: 'dbOwner', db: 'test'}]})
  4. Ran this python code:
    
    from twisted.internet.task import react
    from twisted.internet import defer
    from txmongo.connection import ConnectionPool

@defer.inlineCallbacks def main(reactor): conn = ConnectionPool('mongodb://test:test@localhost/test') yield conn.test.qwe.insert({'x': 42}) yield conn.test.qwe.rename('rty') result = yield conn.test.rty.find() print result yield conn.disconnect()

react(main)


It printed `[{u'x': 42}]` as expected and `rty` collection remains in the database

> mongo has two renameCollection's

They are actually the same: first one is the low-level DB command, second one is Mongo Shell's wrapper written in JavaScript. I've cited its source code above and you can see that it simply runs underlying `renameCollection` DB command.

Could you please try to reproduce the issue on a clean database and publish DB's user accounts setup and testing code?