Skip to content

Commit

Permalink
added support for the user management commands for server 2.6.
Browse files Browse the repository at this point in the history
  • Loading branch information
craiggwilson committed Nov 7, 2013
1 parent 0647d62 commit 5fbcc15
Show file tree
Hide file tree
Showing 6 changed files with 207 additions and 53 deletions.
Expand Up @@ -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
Expand Down
8 changes: 6 additions & 2 deletions MongoDB.Driver/FeatureId.cs
Expand Up @@ -39,6 +39,10 @@ public enum FeatureId
/// <summary>
/// The max time feature.
/// </summary>
MaxTime
MaxTime,
/// <summary>
/// The user management commands.
/// </summary>
UserManagementCommands
}
}
}
183 changes: 153 additions & 30 deletions MongoDB.Driver/MongoDatabase.cs
Expand Up @@ -259,17 +259,20 @@ public virtual MongoDatabaseSettings Settings
/// Adds a user to this database.
/// </summary>
/// <param name="user">The user.</param>
[Obsolete("Use the new user management command 'createUser' or 'updateUser'.")]

This comment has been minimized.

Copy link
@alexjamesbrown

alexjamesbrown Apr 8, 2014

Contributor

@craiggwilson This has been marked obsolete, but I can't see a method to call in its place?

Someone on SO asked a question regarding this - http://stackoverflow.com/questions/22945020/how-to-create-a-user-in-mongodb

This comment has been minimized.

Copy link
@craiggwilson

craiggwilson Apr 8, 2014

Author Contributor

Yes, no new methods. The new user model in 2.4/2.6 is highly complex and has been evolving. We are holding off on creating strongly-typed versions of these until they are stable. So, as the obsolete method mentions, using the createUser and updateUser commands are the correct way to handle this. You can use MongoDatabase.RunCommand to execute these.

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);
}

/// <summary>
Expand Down Expand Up @@ -467,40 +470,36 @@ public virtual object FetchDBRefAs(Type documentType, MongoDBRef dbRef)
/// Finds all users of this database.
/// </summary>
/// <returns>An array of users.</returns>
[Obsolete("Use the new user management command 'usersInfo'.")]
public virtual MongoUser[] FindAllUsers()
{
var result = new List<MongoUser>();
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();
}
}

/// <summary>
/// Finds a user of this database.
/// </summary>
/// <param name="username">The username.</param>
/// <returns>The user.</returns>
[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);
}
}

Expand Down Expand Up @@ -796,6 +795,7 @@ public virtual bool IsCollectionNameValid(string collectionName, out string mess
/// Removes a user from this database.
/// </summary>
/// <param name="user">The user to remove.</param>
[Obsolete("Use RunCommand with a { dropUser: <username> } document.")]
public virtual void RemoveUser(MongoUser user)
{
RemoveUser(user.Username);
Expand All @@ -805,10 +805,21 @@ public virtual void RemoveUser(MongoUser user)
/// Removes a user from this database.
/// </summary>
/// <param name="username">The username to remove.</param>
[Obsolete("Use RunCommand with a { dropUser: <username> } 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));
}
}
}

/// <summary>
Expand Down Expand Up @@ -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<MongoUser>();
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<MongoUser>();
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<TCommandResult>(
IMongoCommand command,
IBsonSerializer resultSerializer,
Expand Down
1 change: 1 addition & 0 deletions MongoDB.Driver/MongoUser.cs
Expand Up @@ -21,6 +21,7 @@ namespace MongoDB.Driver
/// Represents a MongoDB user.
/// </summary>
[Serializable]
[Obsolete("Use the new user management commands instead.")]
public class MongoUser : IEquatable<MongoUser>
{
// private fields
Expand Down
63 changes: 43 additions & 20 deletions MongoDB.DriverUnitTests/MongoDatabaseTests.cs
Expand Up @@ -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
}
}
}
}
}
2 changes: 2 additions & 0 deletions MongoDB.DriverUnitTests/MongoUserTests.cs
Expand Up @@ -16,6 +16,7 @@
using MongoDB.Driver;
using NUnit.Framework;

#pragma warning disable 618
namespace MongoDB.DriverUnitTests
{
[TestFixture]
Expand Down Expand Up @@ -87,3 +88,4 @@ public void TestEquals()
}
}
}
#pragma warning restore

0 comments on commit 5fbcc15

Please sign in to comment.