Skip to content

Commit 5fbcc15

Browse files
committed
added support for the user management commands for server 2.6.
1 parent 0647d62 commit 5fbcc15

File tree

6 files changed

+207
-53
lines changed

6 files changed

+207
-53
lines changed

MongoDB.Driver/Communication/FeatureDetection/FeatureSetDetector.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ internal class FeatureSetDetector
3232
new ServerParameterDependency("enableExperimentalWriteCommands")), // and for now must be explicitly enabled in the server
3333

3434
// added in 2.5.3
35-
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
35+
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
36+
new FeatureDetector(FeatureId.UserManagementCommands, new ServerVersionDependency(2, 5, 3))
3637
};
3738

3839
// public methods

MongoDB.Driver/FeatureId.cs

+6-2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ public enum FeatureId
3939
/// <summary>
4040
/// The max time feature.
4141
/// </summary>
42-
MaxTime
42+
MaxTime,
43+
/// <summary>
44+
/// The user management commands.
45+
/// </summary>
46+
UserManagementCommands
4347
}
44-
}
48+
}

MongoDB.Driver/MongoDatabase.cs

+153-30
Original file line numberDiff line numberDiff line change
@@ -259,17 +259,20 @@ public virtual MongoCollection<BsonDocument> this[string collectionName]
259259
/// Adds a user to this database.
260260
/// </summary>
261261
/// <param name="user">The user.</param>
262+
[Obsolete("Use the new user management command 'createUser' or 'updateUser'.")]
262263
public virtual void AddUser(MongoUser user)
263264
{
264-
var users = GetCollection("system.users");
265-
var document = users.FindOne(Query.EQ("user", user.Username));
266-
if (document == null)
265+
using (RequestStart(ReadPreference.Primary))
267266
{
268-
document = new BsonDocument("user", user.Username);
267+
if (_server.RequestConnection.ServerInstance.Supports(FeatureId.UserManagementCommands))
268+
{
269+
AddUserWithUserManagementCommands(user);
270+
}
271+
else
272+
{
273+
AddUserWithInsert(user);
274+
}
269275
}
270-
document["readOnly"] = user.IsReadOnly;
271-
document["pwd"] = user.PasswordHash;
272-
users.Save(document);
273276
}
274277

275278
/// <summary>
@@ -467,40 +470,36 @@ public virtual object FetchDBRefAs(Type documentType, MongoDBRef dbRef)
467470
/// Finds all users of this database.
468471
/// </summary>
469472
/// <returns>An array of users.</returns>
473+
[Obsolete("Use the new user management command 'usersInfo'.")]
470474
public virtual MongoUser[] FindAllUsers()
471475
{
472-
var result = new List<MongoUser>();
473-
var users = GetCollection("system.users");
474-
foreach (var document in users.FindAll())
476+
using (RequestStart(ReadPreference.Primary))
475477
{
476-
var username = document["user"].AsString;
477-
var passwordHash = document["pwd"].AsString;
478-
var readOnly = document["readOnly"].ToBoolean();
479-
var user = new MongoUser(username, passwordHash, readOnly);
480-
result.Add(user);
481-
};
482-
return result.ToArray();
478+
if (_server.RequestConnection.ServerInstance.Supports(FeatureId.UserManagementCommands))
479+
{
480+
return FindAllUsersWithUserManagementCommands();
481+
}
482+
483+
return FindAllUsersWithQuery();
484+
}
483485
}
484486

485487
/// <summary>
486488
/// Finds a user of this database.
487489
/// </summary>
488490
/// <param name="username">The username.</param>
489491
/// <returns>The user.</returns>
492+
[Obsolete("Use the new user management command 'usersInfo'.")]
490493
public virtual MongoUser FindUser(string username)
491494
{
492-
var users = GetCollection("system.users");
493-
var query = Query.EQ("user", username);
494-
var document = users.FindOne(query);
495-
if (document != null)
496-
{
497-
var passwordHash = document["pwd"].AsString;
498-
var readOnly = document["readOnly"].ToBoolean();
499-
return new MongoUser(username, passwordHash, readOnly);
500-
}
501-
else
495+
using (RequestStart(ReadPreference.Primary))
502496
{
503-
return null;
497+
if (_server.RequestConnection.ServerInstance.Supports(FeatureId.UserManagementCommands))
498+
{
499+
return FindUserWithUserManagementCommands(username);
500+
}
501+
502+
return FindUserWithQuery(username);
504503
}
505504
}
506505

@@ -796,6 +795,7 @@ public virtual bool IsCollectionNameValid(string collectionName, out string mess
796795
/// Removes a user from this database.
797796
/// </summary>
798797
/// <param name="user">The user to remove.</param>
798+
[Obsolete("Use RunCommand with a { dropUser: <username> } document.")]
799799
public virtual void RemoveUser(MongoUser user)
800800
{
801801
RemoveUser(user.Username);
@@ -805,10 +805,21 @@ public virtual void RemoveUser(MongoUser user)
805805
/// Removes a user from this database.
806806
/// </summary>
807807
/// <param name="username">The username to remove.</param>
808+
[Obsolete("Use RunCommand with a { dropUser: <username> } document.")]
808809
public virtual void RemoveUser(string username)
809810
{
810-
var users = GetCollection("system.users");
811-
users.Remove(Query.EQ("user", username));
811+
using (RequestStart(ReadPreference.Primary))
812+
{
813+
if (_server.RequestConnection.ServerInstance.Supports(FeatureId.UserManagementCommands))
814+
{
815+
RunCommand(new CommandDocument("dropUser", username));
816+
}
817+
else
818+
{
819+
var users = GetCollection("system.users");
820+
users.Remove(Query.EQ("user", username));
821+
}
822+
}
812823
}
813824

814825
/// <summary>
@@ -1009,6 +1020,118 @@ public override string ToString()
10091020
}
10101021

10111022
// private methods
1023+
#pragma warning disable 618
1024+
private void AddUserWithInsert(MongoUser user)
1025+
{
1026+
var users = GetCollection("system.users");
1027+
var document = users.FindOne(Query.EQ("user", user.Username));
1028+
if (document == null)
1029+
{
1030+
document = new BsonDocument("user", user.Username);
1031+
}
1032+
document["readOnly"] = user.IsReadOnly;
1033+
document["pwd"] = user.PasswordHash;
1034+
users.Save(document);
1035+
}
1036+
#pragma warning restore
1037+
1038+
#pragma warning disable 618
1039+
private void AddUserWithUserManagementCommands(MongoUser user)
1040+
{
1041+
var usersInfo = RunCommand(new CommandDocument("usersInfo", user.Username));
1042+
1043+
var roles = new BsonArray();
1044+
if (_name == "admin")
1045+
{
1046+
roles.Add(user.IsReadOnly ? "readAnyDatabase" : "root");
1047+
}
1048+
else
1049+
{
1050+
roles.Add(user.IsReadOnly ? "read" : "dbOwner");
1051+
}
1052+
1053+
var commandName = "createUser";
1054+
1055+
if (usersInfo.Response.Contains("users") && usersInfo.Response["users"].AsBsonArray.Count > 0)
1056+
{
1057+
commandName = "updateUser";
1058+
}
1059+
1060+
var userCommand = new CommandDocument
1061+
{
1062+
{ commandName, user.Username },
1063+
{ "pwd", user.PasswordHash },
1064+
{ "digestPassword", false },
1065+
{ "roles", roles }
1066+
};
1067+
1068+
RunCommand(userCommand);
1069+
}
1070+
#pragma warning restore
1071+
1072+
#pragma warning disable 618
1073+
private MongoUser FindUserWithQuery(string username)
1074+
{
1075+
var users = GetCollection("system.users");
1076+
var query = Query.EQ("user", username);
1077+
var document = users.FindOne(query);
1078+
if (document != null)
1079+
{
1080+
var passwordHash = document.GetValue("pwd", "").AsString;
1081+
var readOnly = document["readOnly"].ToBoolean();
1082+
return new MongoUser(username, passwordHash, readOnly);
1083+
}
1084+
1085+
return null;
1086+
}
1087+
#pragma warning restore
1088+
1089+
#pragma warning disable 618
1090+
private MongoUser FindUserWithUserManagementCommands(string username)
1091+
{
1092+
var usersInfoResult = RunCommand(new CommandDocument("usersInfo", username));
1093+
if (usersInfoResult.Response.Contains("users") && usersInfoResult.Response["users"].AsBsonArray.Count > 0)
1094+
{
1095+
return new MongoUser(username, new PasswordEvidence(""), false);
1096+
}
1097+
1098+
return null;
1099+
}
1100+
#pragma warning restore
1101+
1102+
#pragma warning disable 618
1103+
private MongoUser[] FindAllUsersWithQuery()
1104+
{
1105+
var results = new List<MongoUser>();
1106+
var users = GetCollection("system.users");
1107+
foreach (var document in users.FindAll())
1108+
{
1109+
var username = document["user"].AsString;
1110+
var passwordHash = document.GetValue("pwd", "").AsString;
1111+
var readOnly = document["readOnly"].ToBoolean();
1112+
var user = new MongoUser(username, passwordHash, readOnly);
1113+
results.Add(user);
1114+
};
1115+
return results.ToArray();
1116+
}
1117+
#pragma warning restore
1118+
1119+
#pragma warning disable 618
1120+
private MongoUser[] FindAllUsersWithUserManagementCommands()
1121+
{
1122+
var results = new List<MongoUser>();
1123+
var usersInfoResult = RunCommand(new CommandDocument("usersInfo", 1));
1124+
if (usersInfoResult.Response.Contains("users"))
1125+
{
1126+
foreach (var document in usersInfoResult.Response["users"].AsBsonArray)
1127+
{
1128+
results.Add(new MongoUser(document["user"].AsString, new PasswordEvidence(""), false));
1129+
}
1130+
}
1131+
return results.ToArray();
1132+
}
1133+
#pragma warning restore
1134+
10121135
private TCommandResult RunCommandAs<TCommandResult>(
10131136
IMongoCommand command,
10141137
IBsonSerializer resultSerializer,

MongoDB.Driver/MongoUser.cs

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ namespace MongoDB.Driver
2121
/// Represents a MongoDB user.
2222
/// </summary>
2323
[Serializable]
24+
[Obsolete("Use the new user management commands instead.")]
2425
public class MongoUser : IEquatable<MongoUser>
2526
{
2627
// private fields

MongoDB.DriverUnitTests/MongoDatabaseTests.cs

+43-20
Original file line numberDiff line numberDiff line change
@@ -334,26 +334,49 @@ public void TestSetProfilingLevel()
334334
}
335335

336336
[Test]
337-
public void TestUserMethods()
337+
[TestCase("user1", "pass1", true)]
338+
[TestCase("user2", "pass2", false)]
339+
public void TestUserMethods(string username, string password, bool isReadOnly)
338340
{
339-
var collection = _database.GetCollection("system.users");
340-
collection.RemoveAll();
341-
_database.AddUser(new MongoUser("username", new PasswordEvidence("password"), true));
342-
Assert.AreEqual(1, collection.Count());
343-
344-
var user = _database.FindUser("username");
345-
Assert.AreEqual("username", user.Username);
346-
Assert.AreEqual(MongoUtils.Hash("username:mongo:password"), user.PasswordHash);
347-
Assert.AreEqual(true, user.IsReadOnly);
348-
349-
var users = _database.FindAllUsers();
350-
Assert.AreEqual(1, users.Length);
351-
Assert.AreEqual("username", users[0].Username);
352-
Assert.AreEqual(MongoUtils.Hash("username:mongo:password"), users[0].PasswordHash);
353-
Assert.AreEqual(true, users[0].IsReadOnly);
354-
355-
_database.RemoveUser(user);
356-
Assert.AreEqual(0, collection.Count());
341+
#pragma warning disable 618
342+
using (_database.RequestStart(ReadPreference.Primary))
343+
{
344+
bool usesCommands = _database.Server.RequestConnection.ServerInstance.Supports(FeatureId.UserManagementCommands);
345+
if (usesCommands)
346+
{
347+
_database.RunCommand("dropAllUsersFromDatabase");
348+
}
349+
else
350+
{
351+
var collection = _database.GetCollection("system.users");
352+
collection.RemoveAll();
353+
}
354+
355+
_database.AddUser(new MongoUser(username, new PasswordEvidence(password), isReadOnly));
356+
357+
var user = _database.FindUser(username);
358+
Assert.IsNotNull(user);
359+
Assert.AreEqual(username, user.Username);
360+
if (!usesCommands)
361+
{
362+
Assert.AreEqual(MongoUtils.Hash(string.Format("{0}:mongo:{1}", username, password)), user.PasswordHash);
363+
Assert.AreEqual(isReadOnly, user.IsReadOnly);
364+
}
365+
366+
var users = _database.FindAllUsers();
367+
Assert.AreEqual(1, users.Length);
368+
Assert.AreEqual(username, users[0].Username);
369+
if (!usesCommands)
370+
{
371+
Assert.AreEqual(MongoUtils.Hash(string.Format("{0}:mongo:{1}", username, password)), users[0].PasswordHash);
372+
Assert.AreEqual(isReadOnly, users[0].IsReadOnly);
373+
}
374+
375+
_database.RemoveUser(user);
376+
user = _database.FindUser(username);
377+
Assert.IsNull(user);
378+
#pragma warning restore
379+
}
357380
}
358381
}
359-
}
382+
}

MongoDB.DriverUnitTests/MongoUserTests.cs

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
using MongoDB.Driver;
1717
using NUnit.Framework;
1818

19+
#pragma warning disable 618
1920
namespace MongoDB.DriverUnitTests
2021
{
2122
[TestFixture]
@@ -87,3 +88,4 @@ public void TestEquals()
8788
}
8889
}
8990
}
91+
#pragma warning restore

0 commit comments

Comments
 (0)