flapdoodle-oss / de.flapdoodle.embed.mongo

...will provide a platform neutral way for running mongodb in unittests.
Apache License 2.0
910 stars 160 forks source link

Access control: listCollections action not implemented #442

Closed rechvs closed 1 year ago

rechvs commented 1 year ago

Granting a user a role that includes the listCollections action (e.g., the readAnyDatabase role) does not actually allow that user to perform the listCollections command. Performing the command instead throws a MongoCommandException:

Command failed with error 13 (Unauthorized): 'not authorized on customdb-database to execute command { listCollections: 1, cursor: {}, nameOnly: true, $db: "customdb-database", lsid: { id: UUID("4f6c09aa-836a-44c1-b13d-c528e07aacf4") } }' on server localhost:41819. The full response is {"ok": 0.0, "errmsg": "not authorized on customdb-database to execute command { listCollections: 1, cursor: {}, nameOnly: true, $db: \"customdb-database\", lsid: { id: UUID(\"4f6c09aa-836a-44c1-b13d-c528e07aacf4\") } }", "code": 13, "codeName": "Unauthorized"}

michaelmosmann commented 1 year ago

@rechvs now i had some time to look into this.. are you using https://github.com/flapdoodle-oss/de.flapdoodle.embed.mongo.spring with user and password set as in this test: https://github.com/flapdoodle-oss/de.flapdoodle.embed.mongo.spring/blob/main/src/test/java/de/flapdoodle/embed/mongo/spring/autoconfigure/EmbeddedMongoAutoConfigurationTest.java#L194 ?

rechvs commented 1 year ago

Thank you for looking into this.
I’ve finally had time to set up an example project. To answer your question: no, I don’t use the Spring integration but rather setup everything manually (including role and user creation), so it’s very well possible that I’m screwing up there. The project actually exemplifies three types of unexepected behavior (I’ve conflated the first and second one in my first comment, sorry about that):

  1. a custom role that includes the listCollections action but doesn’t actually grant permission to execute the com.mongodb.client.MongoDatabase::listCollectionNames method (see Issue442Test::customRole) This is the main concern of this issue.
  2. the readAnyDatabase role doesn’t seem to be implemented (see Issue442Test::readAnyDatabaseRole))
  3. a test throws a de.flapdoodle.reverse.TearDownException at the end (see Issue442Test::readRole))
michaelmosmann commented 1 year ago

@rechvs thanks.. i will have a look into this sample project..

rechvs commented 1 year ago

I forgot to add this in my last comment: this is the offical documentation for the listCollections action.

michaelmosmann commented 1 year ago

@rechvs no problem..

michaelmosmann commented 1 year ago

@rechvs .. thanks for your example project.. one problem with this: as i try to reduce my dependency footprint i can shutdown a mongodb server simply by sending a binary stream of the shutdown command to an instance without using any driver.. UNTIL no auth is used .. that's why the shutdown fails if you set users with roles..

maybe i have a workaround for this.. i did some trickery in my spring adapter (call shutdown by driver, ignoring shutdown failure in teardown) .. but i hope i can solve this better then that.. stay tuned.

michaelmosmann commented 1 year ago

@rechvs i would like to use the testcode from your example as part of this project.. if this is ok? and would add an author line for this..

michaelmosmann commented 1 year ago

@rechvs as i understand this the readAnyDatabase can not be assigned to a new user https://www.mongodb.com/docs/manual/reference/built-in-roles/#readAnyDatabase

rechvs commented 1 year ago

but i hope i can solve this better then that.. stay tuned.

:+1:

i would like to use the testcode from your example as part of this project.. if this is ok?

Sure, I’m glad I managed to cobble together something useful for this project. :slightly_smiling_face:

as i understand this the readAnyDatabase can not be assigned to a new user

Maybe you’re right: it seems that while users can be created on an arbitrary (non-admin) database, the readAnyDatabase role (like all all-database roles) only exists on the admin database. So if we want to eventually grant a user one of the all-database roles, we must create that user on the admin database? I added a commit to the example project that seems to verify this: the assertions of the readAnyDatabaseRole test now work, but the test throws a de.flapdoodle.reverse.TearDownException at the end. On the other hand, here the docs explicilty say (emphasis mine)

The database where you create the user (in this example, test) is that user's authentication database. Although the user authenticates to this database, the user can have roles in other databases. The user's authentication database does not limit the user's privileges.

But maybe I’m misunderstanding the roles in other dabatases part and it only means that users can have roles that grant access to other databases, while I initially thought it meant that users can have roles that are defined in other databases.

michaelmosmann commented 1 year ago

@rechvs in teardown i try to stop the mongodb by sending a shutdown command to the database, which does not work if auth==true .. i am working on this..

rechvs commented 1 year ago

I think I found out what was wrong with my attempt to grant the readAnyDatabase role: when granting the role (either via createUser or via grantRolesToUser) you can specify the role as a document like this:
{ role: "<role>", db: "<database>" }
So if you want to grant the readAnyDatabase (which only exists on the admin database), you would specify the role like this (credit goes to @ydylla for pointing me in the right direction):
{"role": "readAnyDatabase", "db": "admin"}
I’ve adjusted the corresponding test in the example project accordingly. However, the test now fails with
com.mongodb.MongoSecurityException: Exception authenticating MongoCredential{mechanism=SCRAM-SHA-1, userName='test-db-user', source='admin', password=, mechanismProperties=},
which I think is a flapdoodle issue.

michaelmosmann commented 1 year ago

@rechvs i will have to look into this change.. thanks for your efforts:)

michaelmosmann commented 1 year ago

@rechvs hmm... did you tried this directly on a mongodb? without this java-test-stuff? I am by far no expert on these issues...

rechvs commented 1 year ago

No, not yet. I’ll verify the expected behavior against a normal MongoDB instance, but it might be a while until I have some time on my hands.

michaelmosmann commented 1 year ago

@rechvs no hurry:)

michaelmosmann commented 1 year ago

@rechvs .. any progress with that?

rechvs commented 1 year ago

Sorry, not yet. I’m afraid I won’t have time for this before the end of February. :frowning_face: If keeping the issue open for a currently unknown time bothers you, feel free to close it. If it’s ok for you, I would reopen it as soon as I can work on it some more.

michaelmosmann commented 1 year ago

@rechvs .. no problem:) it stays open..

rechvs commented 1 year ago

I’ve finally managed to verify that the behavior expected by me is actually the appropriate behavior.

I’ve attached a ZIP of shell scripts that’ll hopefully also help you in verifying the expected behavior (at least if you’re on Linux with systemd):

  1. Ensure that access control is deactivated in your local MongoDB instance.
    Judging from these docs it should be enough to start the instance without explicitly activating access control.
  2. Execute ./mongodb-create-collections-without-auth.
  3. Execute ./mongodb-create-user-and-role-without-auth.
  4. Activate access control in your local MongoDB instance.
    On my machine (Debian 4.19.67-2+deb10u2 (2019-11-11) x86_64 GNU/Linux with systemd) the relevant config file is /etc/mongod.conf
  5. Execute ./stop-mongod-via-systemd; ./start-mongod-via-systemd to restart your local MongoDB instance.
  6. Execute ./mongodb-list-collections-with-auth.
    This should return something like the following:

    {
        "cursor" : {
                "id" : NumberLong(0),
                "ns" : "issue-442-test-db.$cmd.listCollections",
                "firstBatch" : [
                        {
                                "name" : "issue-442-test-coll-1",
                                "type" : "collection",
                                "options" : {
    
                                },
                                "info" : {
                                        "readOnly" : false,
                                        "uuid" : UUID("545ccde1-4362-4326-aeec-1798ecfb93d2")
                                },
                                "idIndex" : {
                                        "v" : 2,
                                        "key" : {
                                                "_id" : 1
                                        },
                                        "name" : "_id_"
                                }
                        },
                        {
                                "name" : "issue-442-test-coll-2",
                                "type" : "collection",
                                "options" : {
    
                                },
                                "info" : {
                                        "readOnly" : false,
                                        "uuid" : UUID("70d8c260-b933-45dd-92bd-37d70578cf6d")
                                },
                                "idIndex" : {
                                        "v" : 2,
                                        "key" : {
                                                "_id" : 1
                                        },
                                        "name" : "_id_"
                                }
                        }
                ]
        },
        "ok" : 1
    }
michaelmosmann commented 1 year ago

@rechvs awesome .. so i can try to reproduce this.. :) thanks a lot..

michaelmosmann commented 1 year ago

@rechvs i could create a test from that .. https://github.com/flapdoodle-oss/de.flapdoodle.embed.mongo/blob/main/src/test/java/de/flapdoodle/embed/mongo/scenario/AccessControlAndListCollectionsActionTest.java .. now i can check which modification will break this:)

IMHO this should be possible in one go.. without a server restart.. but maybe not.

rechvs commented 1 year ago

IMHO this should be possible in one go

I’m not sure. If I understand the procedure for SCRAM correctly, you have to start the instance without access control in order to be able to at least create the user admin (or whatever users you need for your test scenario), which you can then (when access control is enabled) use to create all other users. I don’t see a way around this. :frowning_face:

michaelmosmann commented 1 year ago

@rechvs .. i will try:) and then i have a test for that.. :) .. or a test that shows that it does not work..

michaelmosmann commented 1 year ago

@rechvs did update the test.. works in one go .. there are now to tests, hope it helps to spot the difference.. so i try to make this work in my spring adapter too:) thanks for your work and very helpful input:)

michaelmosmann commented 1 year ago

@rechvs after some fun with spring i could create this tests: https://github.com/flapdoodle-oss/de.flapdoodle.embed.mongo.spring/blob/main/src/test/java/de/flapdoodle/embed/mongo/spring/autoconfigure/AuthTest.java .. which now works with spring3.x, spring2.7.x and spring2.6.x .. i had to upgrade the mongodb drivers to make it work with spring2.7.x and spring2.6.x .. which was kind of surprise ..

.. and after all this i might wonder if the problem was a mongodb driver issue and not something we did wrong..

.. anyway. could remove a lot of code.. :) thanks for this inspiration:) i close this issue.. just reopen or create a new if still something is wrong.