diff --git a/MongoDB.Driver/Communication/FeatureDetection/FeatureSetDetector.cs b/MongoDB.Driver/Communication/FeatureDetection/FeatureSetDetector.cs index 9de04a399df..2f812f8dc40 100644 --- a/MongoDB.Driver/Communication/FeatureDetection/FeatureSetDetector.cs +++ b/MongoDB.Driver/Communication/FeatureDetection/FeatureSetDetector.cs @@ -32,7 +32,8 @@ internal class FeatureSetDetector new ServerParameterDependency("enableExperimentalWriteCommands")), // and for now must be explicitly enabled in the server // added in 2.5.3 - new FeatureDetector(FeatureId.MaxTime, new ServerVersionDependency(2, 5, 3)) // while MaxTime was added in 2.5.2 the FailPoint for it wasn't added until 2.5.3 + new FeatureDetector(FeatureId.MaxTime, new ServerVersionDependency(2, 5, 3)), // while MaxTime was added in 2.5.2 the FailPoint for it wasn't added until 2.5.3 + new FeatureDetector(FeatureId.UserManagementCommands, new ServerVersionDependency(2, 5, 3)) }; // public methods diff --git a/MongoDB.Driver/FeatureId.cs b/MongoDB.Driver/FeatureId.cs index ea2c6cae8ec..351ab6bc3f7 100644 --- a/MongoDB.Driver/FeatureId.cs +++ b/MongoDB.Driver/FeatureId.cs @@ -39,6 +39,10 @@ public enum FeatureId /// /// The max time feature. /// - MaxTime + MaxTime, + /// + /// The user management commands. + /// + UserManagementCommands } -} +} \ No newline at end of file diff --git a/MongoDB.Driver/MongoDatabase.cs b/MongoDB.Driver/MongoDatabase.cs index 507cf483227..e72d8a29ba4 100644 --- a/MongoDB.Driver/MongoDatabase.cs +++ b/MongoDB.Driver/MongoDatabase.cs @@ -259,17 +259,20 @@ public virtual MongoDatabaseSettings Settings /// Adds a user to this database. /// /// The user. + [Obsolete("Use the new user management command 'createUser' or 'updateUser'.")] public virtual void AddUser(MongoUser user) { - var users = GetCollection("system.users"); - var document = users.FindOne(Query.EQ("user", user.Username)); - if (document == null) + using (RequestStart(ReadPreference.Primary)) { - document = new BsonDocument("user", user.Username); + if (_server.RequestConnection.ServerInstance.Supports(FeatureId.UserManagementCommands)) + { + AddUserWithUserManagementCommands(user); + } + else + { + AddUserWithInsert(user); + } } - document["readOnly"] = user.IsReadOnly; - document["pwd"] = user.PasswordHash; - users.Save(document); } /// @@ -467,19 +470,18 @@ public virtual object FetchDBRefAs(Type documentType, MongoDBRef dbRef) /// Finds all users of this database. /// /// An array of users. + [Obsolete("Use the new user management command 'usersInfo'.")] public virtual MongoUser[] FindAllUsers() { - var result = new List(); - var users = GetCollection("system.users"); - foreach (var document in users.FindAll()) + using (RequestStart(ReadPreference.Primary)) { - var username = document["user"].AsString; - var passwordHash = document["pwd"].AsString; - var readOnly = document["readOnly"].ToBoolean(); - var user = new MongoUser(username, passwordHash, readOnly); - result.Add(user); - }; - return result.ToArray(); + if (_server.RequestConnection.ServerInstance.Supports(FeatureId.UserManagementCommands)) + { + return FindAllUsersWithUserManagementCommands(); + } + + return FindAllUsersWithQuery(); + } } /// @@ -487,20 +489,17 @@ public virtual MongoUser[] FindAllUsers() /// /// The username. /// The user. + [Obsolete("Use the new user management command 'usersInfo'.")] public virtual MongoUser FindUser(string username) { - var users = GetCollection("system.users"); - var query = Query.EQ("user", username); - var document = users.FindOne(query); - if (document != null) - { - var passwordHash = document["pwd"].AsString; - var readOnly = document["readOnly"].ToBoolean(); - return new MongoUser(username, passwordHash, readOnly); - } - else + using (RequestStart(ReadPreference.Primary)) { - return null; + if (_server.RequestConnection.ServerInstance.Supports(FeatureId.UserManagementCommands)) + { + return FindUserWithUserManagementCommands(username); + } + + return FindUserWithQuery(username); } } @@ -796,6 +795,7 @@ public virtual bool IsCollectionNameValid(string collectionName, out string mess /// Removes a user from this database. /// /// The user to remove. + [Obsolete("Use RunCommand with a { dropUser: } document.")] public virtual void RemoveUser(MongoUser user) { RemoveUser(user.Username); @@ -805,10 +805,21 @@ public virtual void RemoveUser(MongoUser user) /// Removes a user from this database. /// /// The username to remove. + [Obsolete("Use RunCommand with a { dropUser: } document.")] public virtual void RemoveUser(string username) { - var users = GetCollection("system.users"); - users.Remove(Query.EQ("user", username)); + using (RequestStart(ReadPreference.Primary)) + { + if (_server.RequestConnection.ServerInstance.Supports(FeatureId.UserManagementCommands)) + { + RunCommand(new CommandDocument("dropUser", username)); + } + else + { + var users = GetCollection("system.users"); + users.Remove(Query.EQ("user", username)); + } + } } /// @@ -1009,6 +1020,118 @@ public override string ToString() } // private methods + #pragma warning disable 618 + private void AddUserWithInsert(MongoUser user) + { + var users = GetCollection("system.users"); + var document = users.FindOne(Query.EQ("user", user.Username)); + if (document == null) + { + document = new BsonDocument("user", user.Username); + } + document["readOnly"] = user.IsReadOnly; + document["pwd"] = user.PasswordHash; + users.Save(document); + } + #pragma warning restore + + #pragma warning disable 618 + private void AddUserWithUserManagementCommands(MongoUser user) + { + var usersInfo = RunCommand(new CommandDocument("usersInfo", user.Username)); + + var roles = new BsonArray(); + if (_name == "admin") + { + roles.Add(user.IsReadOnly ? "readAnyDatabase" : "root"); + } + else + { + roles.Add(user.IsReadOnly ? "read" : "dbOwner"); + } + + var commandName = "createUser"; + + if (usersInfo.Response.Contains("users") && usersInfo.Response["users"].AsBsonArray.Count > 0) + { + commandName = "updateUser"; + } + + var userCommand = new CommandDocument + { + { commandName, user.Username }, + { "pwd", user.PasswordHash }, + { "digestPassword", false }, + { "roles", roles } + }; + + RunCommand(userCommand); + } + #pragma warning restore + + #pragma warning disable 618 + private MongoUser FindUserWithQuery(string username) + { + var users = GetCollection("system.users"); + var query = Query.EQ("user", username); + var document = users.FindOne(query); + if (document != null) + { + var passwordHash = document.GetValue("pwd", "").AsString; + var readOnly = document["readOnly"].ToBoolean(); + return new MongoUser(username, passwordHash, readOnly); + } + + return null; + } + #pragma warning restore + + #pragma warning disable 618 + private MongoUser FindUserWithUserManagementCommands(string username) + { + var usersInfoResult = RunCommand(new CommandDocument("usersInfo", username)); + if (usersInfoResult.Response.Contains("users") && usersInfoResult.Response["users"].AsBsonArray.Count > 0) + { + return new MongoUser(username, new PasswordEvidence(""), false); + } + + return null; + } + #pragma warning restore + + #pragma warning disable 618 + private MongoUser[] FindAllUsersWithQuery() + { + var results = new List(); + var users = GetCollection("system.users"); + foreach (var document in users.FindAll()) + { + var username = document["user"].AsString; + var passwordHash = document.GetValue("pwd", "").AsString; + var readOnly = document["readOnly"].ToBoolean(); + var user = new MongoUser(username, passwordHash, readOnly); + results.Add(user); + }; + return results.ToArray(); + } + #pragma warning restore + + #pragma warning disable 618 + private MongoUser[] FindAllUsersWithUserManagementCommands() + { + var results = new List(); + var usersInfoResult = RunCommand(new CommandDocument("usersInfo", 1)); + if (usersInfoResult.Response.Contains("users")) + { + foreach (var document in usersInfoResult.Response["users"].AsBsonArray) + { + results.Add(new MongoUser(document["user"].AsString, new PasswordEvidence(""), false)); + } + } + return results.ToArray(); + } + #pragma warning restore + private TCommandResult RunCommandAs( IMongoCommand command, IBsonSerializer resultSerializer, diff --git a/MongoDB.Driver/MongoUser.cs b/MongoDB.Driver/MongoUser.cs index c6a3cfa4bf4..a8989889522 100644 --- a/MongoDB.Driver/MongoUser.cs +++ b/MongoDB.Driver/MongoUser.cs @@ -21,6 +21,7 @@ namespace MongoDB.Driver /// Represents a MongoDB user. /// [Serializable] + [Obsolete("Use the new user management commands instead.")] public class MongoUser : IEquatable { // private fields diff --git a/MongoDB.DriverUnitTests/MongoDatabaseTests.cs b/MongoDB.DriverUnitTests/MongoDatabaseTests.cs index 5d5ebd924c3..37fd920bbdd 100644 --- a/MongoDB.DriverUnitTests/MongoDatabaseTests.cs +++ b/MongoDB.DriverUnitTests/MongoDatabaseTests.cs @@ -334,26 +334,49 @@ public void TestSetProfilingLevel() } [Test] - public void TestUserMethods() + [TestCase("user1", "pass1", true)] + [TestCase("user2", "pass2", false)] + public void TestUserMethods(string username, string password, bool isReadOnly) { - var collection = _database.GetCollection("system.users"); - collection.RemoveAll(); - _database.AddUser(new MongoUser("username", new PasswordEvidence("password"), true)); - Assert.AreEqual(1, collection.Count()); - - var user = _database.FindUser("username"); - Assert.AreEqual("username", user.Username); - Assert.AreEqual(MongoUtils.Hash("username:mongo:password"), user.PasswordHash); - Assert.AreEqual(true, user.IsReadOnly); - - var users = _database.FindAllUsers(); - Assert.AreEqual(1, users.Length); - Assert.AreEqual("username", users[0].Username); - Assert.AreEqual(MongoUtils.Hash("username:mongo:password"), users[0].PasswordHash); - Assert.AreEqual(true, users[0].IsReadOnly); - - _database.RemoveUser(user); - Assert.AreEqual(0, collection.Count()); + #pragma warning disable 618 + using (_database.RequestStart(ReadPreference.Primary)) + { + bool usesCommands = _database.Server.RequestConnection.ServerInstance.Supports(FeatureId.UserManagementCommands); + if (usesCommands) + { + _database.RunCommand("dropAllUsersFromDatabase"); + } + else + { + var collection = _database.GetCollection("system.users"); + collection.RemoveAll(); + } + + _database.AddUser(new MongoUser(username, new PasswordEvidence(password), isReadOnly)); + + var user = _database.FindUser(username); + Assert.IsNotNull(user); + Assert.AreEqual(username, user.Username); + if (!usesCommands) + { + Assert.AreEqual(MongoUtils.Hash(string.Format("{0}:mongo:{1}", username, password)), user.PasswordHash); + Assert.AreEqual(isReadOnly, user.IsReadOnly); + } + + var users = _database.FindAllUsers(); + Assert.AreEqual(1, users.Length); + Assert.AreEqual(username, users[0].Username); + if (!usesCommands) + { + Assert.AreEqual(MongoUtils.Hash(string.Format("{0}:mongo:{1}", username, password)), users[0].PasswordHash); + Assert.AreEqual(isReadOnly, users[0].IsReadOnly); + } + + _database.RemoveUser(user); + user = _database.FindUser(username); + Assert.IsNull(user); + #pragma warning restore + } } } -} +} \ No newline at end of file diff --git a/MongoDB.DriverUnitTests/MongoUserTests.cs b/MongoDB.DriverUnitTests/MongoUserTests.cs index 16c4831626c..40838103a08 100644 --- a/MongoDB.DriverUnitTests/MongoUserTests.cs +++ b/MongoDB.DriverUnitTests/MongoUserTests.cs @@ -16,6 +16,7 @@ using MongoDB.Driver; using NUnit.Framework; +#pragma warning disable 618 namespace MongoDB.DriverUnitTests { [TestFixture] @@ -87,3 +88,4 @@ public void TestEquals() } } } +#pragma warning restore \ No newline at end of file