3

I connect to a MongoDB using the C# MongoDB.Driver library. Some code that works

MongoClient client = MongoClientBuilder.Build(
    "77.199.99.90", 27016, "admin", "pwd");
IReadOnlyList<string> dbNames = this.client.GetDatabaseNamesAsync().Result;
foreach (var dbn in dbNames)
{
    IMongoDatabase mongoDb = client.GetDatabase(dbn);
    var cNames = mongoDb.GetCollectionNamesAsync().Result; <- THIS THROWS AggregateException.
    foreach (var cn in cNames)
    {
        ...
    }
}

So the credentials are correct and I get my IMongoDatabases just fine, however, when I attempt to retrieve the collections within a database, I get an AggregateException when I do mongoDb.GetCollectionNamesAsync().Result, the exception details are

AggregateException InnerException (count 1) MongoQueryException: QueryFailure flag was true (response was { "$err" : "not authorized for query on taurusEvents.system.namespaces", "code" : 13 }).

I am not sure what this exception is telling me. The authentication on the client is fine, but I can seem to query the database. What am I missing here?


Edit. I now realise that I needed to use a credential to do this, however, doing

public static MongoClient Build(string host, int port,
    string username, string password, string authDb)
{
    MongoCredential cred = MongoCredential.CreateMongoCRCredential(authDb, username, password);
    MongoClientSettings settings = new MongoClientSettings()
    {
        Credentials = new[] { cred }, 
        Server = new MongoServerAddress(host, port)
    };
    return new MongoClient(settings);
}

where I am now providing my authentication database name authDb also throws the same inner exception above.


Edit#2. I have found that if I do

var o = database.GetCollection<BsonDocument>("events");

with an explicit reference to the collection name, it works, I get my collection. But I want a list of the collections avalible, why is GetCollectionNamesAsync() not working?


Edit#3. I create my server roles using:

1. Create an admin user:

use admin
db.createUser({user: "admin", pwd: "*******", roles: [{role: "userAdminAnyDatabase", db: "admin"}]})

2. Create other users eg:

use admin
db.createUser({user: "mel", pwd: "*******", roles: [{role: "readWrite", db: "taurusEvents"}, {role: "readWrite", db: "taurusOdds"}, {role: "readWrite", db: "taurusState"}]})

Thanks for your time.

MoonKnight
  • 23,214
  • 40
  • 145
  • 277

2 Answers2

1

You are getting an AggregateException because of the way .Result works in .NET.

var result = task.Result; // throws an AggregateException if the task faulted

To have your code throw the inner exception use either:

var result = task.GetAwaiter().GetResult(); // throws the inner exception if the task faulted
var result = await task; // also throws the inner exception if the task faulted

I suspect that the reason you are getting this exception is that while you are using valid credentials (if not you would have failed sooner), the user you are authenticating as does not have permission to read the taurusEvents database.

Robert Stam
  • 12,039
  • 2
  • 39
  • 36
  • Hi Robert, thanks. I understand why I am getting an `AggregateException` as opposed to another type, what I don't understand is why I am getting the `MongoQueryException`. I think I may need to specify the authentication database... – MoonKnight Feb 05 '15 at 18:58
  • It's not enough to have a valid username and password. The username must also have permission to use any databases you are attempting to use. In this case it appears that the username you used to authenticate with doesn't have permission to access the taurusEvents database. See documentation on authorization at: http://docs.mongodb.org/manual/core/authorization/ – Robert Stam Feb 05 '15 at 21:17
  • Thanks, I will check the permissions. The only thing that is confusing me is that I have set the user to "admin" so that it has all access rights available to it. This must have not been done correctly, I will check again... Thanks very much for your time. – MoonKnight Feb 06 '15 at 10:09
1

GetCollection and GetCollectionNamesAsync are different.

GetCollection merely creates a client side object that represents a collection. It doesn't actually talk to the server (so authentication would never be an issue).

GetCollectionNamesAsync talks to the server to fetch the names of all the collections for a given database. This requires that the username you authenticated with have at least read permission on that database.

Robert Stam
  • 12,039
  • 2
  • 39
  • 36
  • FYI: in the latest version (beta2) GetCollectionNamesAsync has been replaced by ListCollectionsAsync, which returns a cursor of documents describing the collections (the "name" element of those documents contains the name of the collection). – Robert Stam Feb 05 '15 at 21:31
  • I will take a look, thanks again. I have edited the question with what I use to create users, can you see anything obviously wrong with this? – MoonKnight Feb 06 '15 at 11:11