Skip to content
Browse files

CSHARP-603: added support for setting credentials via url.

  • Loading branch information...
1 parent 38b650f commit d8fda449ceec21f7d22928e8877a121d984b8832 @craiggwilson craiggwilson committed
View
2 MongoDB.Bson/MongoDB.Bson.csproj
@@ -5,7 +5,7 @@
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
- <ProjectGuid>{0E9A3A2A-49CD-4F6C-847C-DC79B4B65CE6}</ProjectGuid>
+ <ProjectGuid>{525A4B3A-5011-4DE8-94F1-0B6DFC03BC61}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>MongoDB.Bson</RootNamespace>
View
611 MongoDB.Driver/Communication/Security/MongoConnection.cs
@@ -0,0 +1,611 @@
+///* Copyright 2010-2012 10gen Inc.
+//*
+//* Licensed under the Apache License, Version 2.0 (the "License");
+//* you may not use this file except in compliance with the License.
+//* You may obtain a copy of the License at
+//*
+//* http://www.apache.org/licenses/LICENSE-2.0
+//*
+//* Unless required by applicable law or agreed to in writing, software
+//* distributed under the License is distributed on an "AS IS" BASIS,
+//* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//* See the License for the specific language governing permissions and
+//* limitations under the License.
+//*/
+
+//using System;
+//using System.Collections.Generic;
+//using System.IO;
+//using System.Linq;
+//using System.Net;
+//using System.Net.Security;
+//using System.Net.Sockets;
+//using System.Security.Cryptography.X509Certificates;
+//using System.Text;
+
+//using MongoDB.Bson;
+//using MongoDB.Bson.IO;
+//using MongoDB.Bson.Serialization;
+//using MongoDB.Driver.Security;
+
+//namespace MongoDB.Driver.Internal
+//{
+// /// <summary>
+// /// Represents the state of a connection.
+// /// </summary>
+// public enum MongoConnectionState
+// {
+// /// <summary>
+// /// The connection has not yet been initialized.
+// /// </summary>
+// Initial,
+// /// <summary>
+// /// The connection is open.
+// /// </summary>
+// Open,
+// /// <summary>
+// /// The connection is closed.
+// /// </summary>
+// Closed
+// }
+
+// /// <summary>
+// /// Represents a connection to a MongoServerInstance.
+// /// </summary>
+// public class MongoConnection
+// {
+// // private fields
+// private object _connectionLock = new object();
+// private MongoServerInstance _serverInstance;
+// private MongoConnectionPool _connectionPool;
+// private int _generationId; // the generationId of the connection pool at the time this connection was created
+// private MongoConnectionState _state;
+// private TcpClient _tcpClient;
+// private Stream _stream; // either a NetworkStream or an SslStream wrapping a NetworkStream
+// private DateTime _createdAt;
+// private DateTime _lastUsedAt; // set every time the connection is Released
+// private int _messageCounter;
+// private int _requestId;
+// private Dictionary<string, Authentication> _authentications = new Dictionary<string, Authentication>();
+
+// // constructors
+// internal MongoConnection(MongoConnectionPool connectionPool)
+// {
+// _serverInstance = connectionPool.ServerInstance;
+// _connectionPool = connectionPool;
+// _generationId = connectionPool.GenerationId;
+// _createdAt = DateTime.UtcNow;
+// _state = MongoConnectionState.Initial;
+// }
+
+// internal MongoConnection(MongoServerInstance serverInstance)
+// {
+// _serverInstance = serverInstance;
+// _createdAt = DateTime.UtcNow;
+// _state = MongoConnectionState.Initial;
+// }
+
+// // public properties
+// /// <summary>
+// /// Gets the connection pool that this connection belongs to.
+// /// </summary>
+// public MongoConnectionPool ConnectionPool
+// {
+// get { return _connectionPool; }
+// }
+
+// /// <summary>
+// /// Gets the DateTime that this connection was created at.
+// /// </summary>
+// public DateTime CreatedAt
+// {
+// get { return _createdAt; }
+// }
+
+// /// <summary>
+// /// Gets the generation of the connection pool that this connection belongs to.
+// /// </summary>
+// public int GenerationId
+// {
+// get { return _generationId; }
+// }
+
+// /// <summary>
+// /// Gets the DateTime that this connection was last used at.
+// /// </summary>
+// public DateTime LastUsedAt
+// {
+// get { return _lastUsedAt; }
+// internal set { _lastUsedAt = value; }
+// }
+
+// /// <summary>
+// /// Gets a count of the number of messages that have been sent using this connection.
+// /// </summary>
+// public int MessageCounter
+// {
+// get { return _messageCounter; }
+// }
+
+// /// <summary>
+// /// Gets the RequestId of the last message sent on this connection.
+// /// </summary>
+// public int RequestId
+// {
+// get { return _requestId; }
+// }
+
+// /// <summary>
+// /// Gets the server instance this connection is connected to.
+// /// </summary>
+// public MongoServerInstance ServerInstance
+// {
+// get { return _serverInstance; }
+// }
+
+// /// <summary>
+// /// Gets the state of this connection.
+// /// </summary>
+// public MongoConnectionState State
+// {
+// get { return _state; }
+// }
+
+// // internal methods
+// internal void Authenticate(MongoDatabase database)
+// {
+// if (_state == MongoConnectionState.Closed) { throw new InvalidOperationException("Connection is closed."); }
+// if (!CanAuthenticate(database))
+// {
+// throw new InvalidOperationException("Database cannot be authenticated using this connection. Ensure that CanAuthenticate is called before calling Authenticate.");
+// }
+
+// if (database.Credentials == null)
+// {
+// return;
+// }
+
+// Authentication authentication;
+// if (_authentications.TryGetValue(database.Name, out authentication))
+// {
+// authentication.LastUsed = DateTime.UtcNow;
+// }
+// else
+// {
+// if (database.Credentials.CredentialsType == MongoCredentialsType.Legacy)
+// {
+// new LegacyAuthenticator().Authenticate(this, database.Name, database.Credentials);
+// }
+// else
+// {
+// new SaslAuthenticator().Authenticate(this, database.Name, database.Credentials);
+// }
+
+// authentication = new Authentication(database.Credentials);
+// _authentications.Add(database.Name, authentication);
+// }
+// }
+
+// // check whether the connection can be used with the given database (and credentials)
+// // the following are the only valid authentication states for a connection:
+// // 1. the connection is not authenticated against any database
+// // 2. the connection has a single authentication against the admin database (with a particular set of credentials)
+// // 3. the connection has one or more authentications against any databases other than admin
+// // (with the restriction that a particular database can only be authenticated against once and therefore with only one set of credentials)
+// // assume that IsAuthenticated was called first and returned false
+// internal bool CanAuthenticate(MongoDatabase database)
+// {
+// if (_state == MongoConnectionState.Closed) { throw new InvalidOperationException("Connection is closed."); }
+// if (database == null)
+// {
+// return true;
+// }
+
+// if (_authentications.Count == 0)
+// {
+// // a connection with no existing authentications can authenticate anything
+// return true;
+// }
+// else
+// {
+// // a connection with existing authentications can't be used without credentials
+// if (database.Credentials == null)
+// {
+// return false;
+// }
+
+// // a connection with existing authentications can't be used with new admin credentials
+// if (database.Credentials.Admin)
+// {
+// return false;
+// }
+
+// // a connection with an existing authentication to the admin database can't be used with any other credentials
+// if (_authentications.ContainsKey("admin"))
+// {
+// return false;
+// }
+
+// // a connection with an existing authentication to a database can't authenticate for the same database again
+// if (_authentications.ContainsKey(database.Name))
+// {
+// return false;
+// }
+
+// return true;
+// }
+// }
+
+// internal void Close()
+// {
+// lock (_connectionLock)
+// {
+// if (_state != MongoConnectionState.Closed)
+// {
+// if (_stream != null)
+// {
+// try { _stream.Close(); }
+// catch { } // ignore exceptions
+// _stream = null;
+// }
+// if (_tcpClient != null)
+// {
+// if (_tcpClient.Connected)
+// {
+// // even though MSDN says TcpClient.Close doesn't close the underlying socket
+// // it actually does (as proven by disassembling TcpClient and by experimentation)
+// try { _tcpClient.Close(); }
+// catch { } // ignore exceptions
+// }
+// _tcpClient = null;
+// }
+// _state = MongoConnectionState.Closed;
+// }
+// }
+// }
+
+// internal bool IsAuthenticated(MongoDatabase database)
+// {
+// if (_state == MongoConnectionState.Closed) { throw new InvalidOperationException("Connection is closed."); }
+// if (database == null)
+// {
+// return true;
+// }
+
+// lock (_connectionLock)
+// {
+// if (database.Credentials == null)
+// {
+// return _authentications.Count == 0;
+// }
+// else
+// {
+// var authenticationDatabaseName = database.Credentials.Admin ? "admin" : database.Name;
+// Authentication authentication;
+// if (_authentications.TryGetValue(authenticationDatabaseName, out authentication))
+// {
+// return database.Credentials == authentication.Credentials;
+// }
+// else
+// {
+// return false;
+// }
+// }
+// }
+// }
+
+// internal void Logout(string databaseName)
+// {
+// if (_state == MongoConnectionState.Closed) { throw new InvalidOperationException("Connection is closed."); }
+// lock (_connectionLock)
+// {
+// var logoutCommand = new CommandDocument("logout", 1);
+// var commandResult = RunCommand(databaseName, QueryFlags.None, logoutCommand, false);
+// if (!commandResult.Ok)
+// {
+// throw new MongoAuthenticationException(
+// "Error logging off.",
+// new MongoCommandException(commandResult));
+// }
+
+// _authentications.Remove(databaseName);
+// }
+// }
+
+// internal void Open()
+// {
+// if (_state != MongoConnectionState.Initial)
+// {
+// throw new InvalidOperationException("Open called more than once.");
+// }
+
+// var ipEndPoint = _serverInstance.GetIPEndPoint();
+// var tcpClient = new TcpClient(ipEndPoint.AddressFamily);
+// tcpClient.NoDelay = true; // turn off Nagle
+// tcpClient.ReceiveBufferSize = MongoDefaults.TcpReceiveBufferSize;
+// tcpClient.SendBufferSize = MongoDefaults.TcpSendBufferSize;
+// tcpClient.Connect(ipEndPoint);
+
+// var stream = (Stream)tcpClient.GetStream();
+// if (_serverInstance.Server.Settings.UseSsl)
+// {
+// SslStream sslStream;
+// if (_serverInstance.Server.Settings.VerifySslCertificate)
+// {
+// sslStream = new SslStream(stream, false); // don't leave inner stream open
+// }
+// else
+// {
+// sslStream = new SslStream(stream, false, AcceptAnyCertificate, null); // don't leave inner stream open
+// }
+
+// try
+// {
+// sslStream.AuthenticateAsClient(_serverInstance.Address.Host);
+// }
+// catch
+// {
+// try { stream.Close(); }
+// catch { } // ignore exceptions
+// try { tcpClient.Close(); }
+// catch { } // ignore exceptions
+// throw;
+// }
+// stream = sslStream;
+// }
+
+// _tcpClient = tcpClient;
+// _stream = stream;
+// _state = MongoConnectionState.Open;
+// }
+
+// // this is a low level method that doesn't require a MongoServer
+// // so it can be used while connecting to a MongoServer
+// internal CommandResult RunCommand(
+// string databaseName,
+// QueryFlags queryFlags,
+// CommandDocument command,
+// bool throwOnError)
+// {
+// var commandName = command.GetElement(0).Name;
+
+// var writerSettings = new BsonBinaryWriterSettings
+// {
+// GuidRepresentation = GuidRepresentation.Unspecified,
+// MaxDocumentSize = _serverInstance.MaxDocumentSize
+// };
+// using (var message = new MongoQueryMessage(writerSettings, databaseName + ".$cmd", queryFlags, 0, 1, command, null))
+// {
+// SendMessage(message, null, databaseName); // write concern doesn't apply to queries
+// }
+
+// var readerSettings = new BsonBinaryReaderSettings
+// {
+// GuidRepresentation = GuidRepresentation.Unspecified,
+// MaxDocumentSize = _serverInstance.MaxDocumentSize
+// };
+// var reply = ReceiveMessage<BsonDocument>(readerSettings, null);
+// if (reply.NumberReturned == 0)
+// {
+// var message = string.Format("Command '{0}' failed. No response returned.", commandName);
+// throw new MongoCommandException(message);
+// }
+
+// var commandResult = new CommandResult(command, reply.Documents[0]);
+// if (throwOnError && !commandResult.Ok)
+// {
+// throw new MongoCommandException(commandResult);
+// }
+
+// return commandResult;
+// }
+
+// internal MongoReplyMessage<TDocument> ReceiveMessage<TDocument>(
+// BsonBinaryReaderSettings readerSettings,
+// IBsonSerializationOptions serializationOptions)
+// {
+// if (_state == MongoConnectionState.Closed) { throw new InvalidOperationException("Connection is closed."); }
+// lock (_connectionLock)
+// {
+// try
+// {
+// using (var buffer = new BsonBuffer())
+// {
+// var networkStream = GetNetworkStream();
+// var readTimeout = (int)_serverInstance.Server.Settings.SocketTimeout.TotalMilliseconds;
+// if (readTimeout != 0)
+// {
+// networkStream.ReadTimeout = readTimeout;
+// }
+// buffer.LoadFrom(networkStream);
+// var reply = new MongoReplyMessage<TDocument>(readerSettings);
+// reply.ReadFrom(buffer, serializationOptions);
+// return reply;
+// }
+// }
+// catch (Exception ex)
+// {
+// HandleException(ex);
+// throw;
+// }
+// }
+// }
+
+// internal WriteConcernResult SendMessage(MongoRequestMessage message, WriteConcern writeConcern, string databaseName)
+// {
+// if (_state == MongoConnectionState.Closed) { throw new InvalidOperationException("Connection is closed."); }
+// lock (_connectionLock)
+// {
+// _requestId = message.RequestId;
+
+// message.WriteToBuffer();
+// CommandDocument getLastErrorCommand = null;
+// if (writeConcern != null && !writeConcern.FireAndForget)
+// {
+// var fsync = (writeConcern.FSync == null) ? null : (BsonValue)writeConcern.FSync;
+// var journal = (writeConcern.Journal == null) ? null : (BsonValue)writeConcern.Journal;
+// var w = (writeConcern.W == null) ? null : writeConcern.W.ToBsonValue();
+// var wTimeout = (writeConcern.WTimeout == null) ? null : (BsonValue)(int)writeConcern.WTimeout.Value.TotalMilliseconds;
+
+// getLastErrorCommand = new CommandDocument
+// {
+// { "getlasterror", 1 }, // use all lowercase for backward compatibility
+// { "fsync", fsync, fsync != null },
+// { "j", journal, journal != null },
+// { "w", w, w != null },
+// { "wtimeout", wTimeout, wTimeout != null }
+// };
+// // piggy back on network transmission for message
+// using (var getLastErrorMessage = new MongoQueryMessage(message.Buffer, message.WriterSettings, databaseName + ".$cmd", QueryFlags.None, 0, 1, getLastErrorCommand, null))
+// {
+// getLastErrorMessage.WriteToBuffer();
+// }
+// }
+
+// try
+// {
+// var networkStream = GetNetworkStream();
+// var writeTimeout = (int)_serverInstance.Server.Settings.SocketTimeout.TotalMilliseconds;
+// if (writeTimeout != 0)
+// {
+// networkStream.WriteTimeout = writeTimeout;
+// }
+// message.Buffer.WriteTo(networkStream);
+// _messageCounter++;
+// }
+// catch (Exception ex)
+// {
+// HandleException(ex);
+// throw;
+// }
+
+// WriteConcernResult writeConcernResult = null;
+// if (writeConcern != null && !writeConcern.FireAndForget)
+// {
+// var readerSettings = new BsonBinaryReaderSettings
+// {
+// GuidRepresentation = message.WriterSettings.GuidRepresentation,
+// MaxDocumentSize = _serverInstance.MaxDocumentSize
+// };
+// var replyMessage = ReceiveMessage<BsonDocument>(readerSettings, null);
+// var getLastErrorResponse = replyMessage.Documents[0];
+// writeConcernResult = new WriteConcernResult();
+// writeConcernResult.Initialize(getLastErrorCommand, getLastErrorResponse);
+
+// if (!writeConcernResult.Ok)
+// {
+// var errorMessage = string.Format(
+// "WriteConcern detected an error '{0}'. (response was {1}).",
+// writeConcernResult.ErrorMessage, getLastErrorResponse.ToJson());
+// throw new WriteConcernException(errorMessage, writeConcernResult);
+// }
+// if (writeConcernResult.HasLastErrorMessage)
+// {
+// var errorMessage = string.Format(
+// "WriteConcern detected an error '{0}'. (Response was {1}).",
+// writeConcernResult.LastErrorMessage, getLastErrorResponse.ToJson());
+// throw new WriteConcernException(errorMessage, writeConcernResult);
+// }
+// }
+
+// return writeConcernResult;
+// }
+// }
+
+// // private methods
+// private bool AcceptAnyCertificate(
+// object sender,
+// X509Certificate certificate,
+// X509Chain chain,
+// SslPolicyErrors sslPolicyErrors
+// )
+// {
+// return true;
+// }
+
+// private Stream GetNetworkStream()
+// {
+// if (_state == MongoConnectionState.Initial)
+// {
+// Open();
+// }
+// return _stream;
+// }
+
+// private void HandleException(Exception ex)
+// {
+// // there are three possible situations:
+// // 1. we can keep using the connection
+// // 2. just this one connection needs to be closed
+// // 3. the whole connection pool needs to be cleared
+
+// switch (DetermineAction(ex))
+// {
+// case HandleExceptionAction.KeepConnection:
+// break;
+// case HandleExceptionAction.CloseConnection:
+// Close();
+// break;
+// case HandleExceptionAction.ClearConnectionPool:
+// Close();
+// if (_connectionPool != null)
+// {
+// _connectionPool.Clear();
+// }
+// break;
+// default:
+// throw new MongoInternalException("Invalid HandleExceptionAction");
+// }
+
+// // forces a call to VerifyState before the next message is sent to this server instance
+// // this is a bit drastic but at least it's safe (and perhaps we can optimize a bit in the future)
+// _serverInstance.SetState(MongoServerState.Unknown);
+// }
+
+// private enum HandleExceptionAction
+// {
+// KeepConnection,
+// CloseConnection,
+// ClearConnectionPool
+// }
+
+// private HandleExceptionAction DetermineAction(Exception ex)
+// {
+// // TODO: figure out when to return KeepConnection or ClearConnectionPool (if ever)
+
+// // don't return ClearConnectionPool unless you are *sure* it is the right action
+// // definitely don't make ClearConnectionPool the default action
+// // returning ClearConnectionPool frequently can result in Connect/Disconnect storms
+
+// return HandleExceptionAction.CloseConnection; // this should always be the default action
+// }
+
+// // private nested classes
+// // keeps track of what credentials were used with a given database
+// // and when that database was last used on this connection
+// private class Authentication
+// {
+// // private fields
+// private MongoCredentials _credentials;
+// private DateTime _lastUsed;
+
+// // constructors
+// public Authentication(MongoCredentials credentials)
+// {
+// _credentials = credentials;
+// _lastUsed = DateTime.UtcNow;
+// }
+
+// public MongoCredentials Credentials
+// {
+// get { return _credentials; }
+// }
+
+// public DateTime LastUsed
+// {
+// get { return _lastUsed; }
+// set { _lastUsed = value; }
+// }
+// }
+// }
+//}
View
44 MongoDB.Driver/MongoClientIdentity.cs
@@ -13,7 +13,7 @@ namespace MongoDB.Driver
public class MongoClientIdentity
{
// private static fields
- public readonly static MongoClientIdentity _system = SystemMongoClientIdentity.Instance;
+ private readonly static MongoClientIdentity _system = SystemMongoClientIdentity.Instance;
// private fields
private readonly MongoAuthenticationType _authenticationType;
@@ -51,7 +51,10 @@ public MongoClientIdentity(string username, string password, MongoAuthentication
public MongoClientIdentity(string username, SecureString password, MongoAuthenticationType authenticationType)
{
_username = username;
- _password = password.Copy();
+ if (password != null)
+ {
+ _password = password.Copy();
+ }
_authenticationType = authenticationType;
}
@@ -67,12 +70,25 @@ public MongoAuthenticationType AuthenticationType
get { return _authenticationType; }
}
+ public bool HasPassword
+ {
+ get { return _password != null; }
+ }
+
/// <summary>
/// Gets the password.
/// </summary>
public string Password
{
- get { return CreateString(_password); }
+ get
+ {
+ if (HasPassword)
+ {
+ return CreateString(_password);
+ }
+
+ return null;
+ }
}
/// <summary>
@@ -80,7 +96,15 @@ public string Password
/// </summary>
public SecureString SecurePassword
{
- get { return _password.Copy(); }
+ get
+ {
+ if (HasPassword)
+ {
+ return _password.Copy();
+ }
+
+ return null;
+ }
}
/// <summary>
@@ -104,13 +128,17 @@ internal string Realm
// private static methods
private static SecureString CreateSecureString(string str)
{
- var secureStr = new SecureString();
- foreach (var c in str)
+ if (str != null)
{
- secureStr.AppendChar(c);
+ var secureStr = new SecureString();
+ foreach (var c in str)
+ {
+ secureStr.AppendChar(c);
+ }
+ return secureStr;
}
- return secureStr;
+ return null;
}
private static string CreateString(SecureString secureStr)
View
4 MongoDB.Driver/MongoClientSettings.cs
@@ -454,6 +454,7 @@ public static MongoClientSettings FromUrl(MongoUrl url)
clientSettings.CredentialsStore = new MongoCredentialsStore();
clientSettings.DefaultCredentials = url.DefaultCredentials;
clientSettings.GuidRepresentation = url.GuidRepresentation;
+ clientSettings.Identity = url.Identity;
clientSettings.IPv6 = url.IPv6;
clientSettings.MaxConnectionIdleTime = url.MaxConnectionIdleTime;
clientSettings.MaxConnectionLifeTime = url.MaxConnectionLifeTime;
@@ -530,6 +531,7 @@ public override bool Equals(object obj)
_credentialsStore.Equals(rhs._credentialsStore) &&
_defaultCredentials == rhs._defaultCredentials &&
_guidRepresentation == rhs._guidRepresentation &&
+ _identity == rhs._identity &&
_ipv6 == rhs._ipv6 &&
_maxConnectionIdleTime == rhs._maxConnectionIdleTime &&
_maxConnectionLifeTime == rhs._maxConnectionLifeTime &&
@@ -601,6 +603,7 @@ public override int GetHashCode()
hash = 37 * hash + _credentialsStore.GetHashCode();
hash = 37 * hash + ((_defaultCredentials == null) ? 0 : _defaultCredentials.GetHashCode());
hash = 37 * hash + _guidRepresentation.GetHashCode();
+ hash = 37 * hash + ((_identity == null) ? 0 : _identity.GetHashCode());
hash = 37 * hash + _ipv6.GetHashCode();
hash = 37 * hash + _maxConnectionIdleTime.GetHashCode();
hash = 37 * hash + _maxConnectionLifeTime.GetHashCode();
@@ -639,6 +642,7 @@ public override string ToString()
sb.AppendFormat("Credentials={{{0}}};", _credentialsStore);
sb.AppendFormat("DefaultCredentials={0};", _defaultCredentials);
sb.AppendFormat("GuidRepresentation={0};", _guidRepresentation);
+ sb.AppendFormat("Identity={0};", _identity);
sb.AppendFormat("IPv6={0};", _ipv6);
sb.AppendFormat("MaxConnectionIdleTime={0};", _maxConnectionIdleTime);
sb.AppendFormat("MaxConnectionLifeTime={0};", _maxConnectionLifeTime);
View
2 MongoDB.Driver/MongoDB.Driver.csproj
@@ -5,7 +5,7 @@
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
- <ProjectGuid>{AE5166CD-76B0-4911-BD80-CED9521F37A1}</ProjectGuid>
+ <ProjectGuid>{65460772-FDD4-40D0-8AEE-FD9A36D3359F}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>MongoDB.Driver</RootNamespace>
View
4 MongoDB.Driver/MongoServerSettings.cs
@@ -616,6 +616,7 @@ public static MongoServerSettings FromUrl(MongoUrl url)
serverSettings.CredentialsStore = new MongoCredentialsStore();
serverSettings.DefaultCredentials = url.DefaultCredentials;
serverSettings.GuidRepresentation = url.GuidRepresentation;
+ serverSettings.Identity = url.Identity;
serverSettings.IPv6 = url.IPv6;
serverSettings.MaxConnectionIdleTime = url.MaxConnectionIdleTime;
serverSettings.MaxConnectionLifeTime = url.MaxConnectionLifeTime;
@@ -694,6 +695,7 @@ public override bool Equals(object obj)
_credentialsStore.Equals(rhs._credentialsStore) &&
_defaultCredentials == rhs._defaultCredentials &&
_guidRepresentation == rhs._guidRepresentation &&
+ _identity == rhs._identity &&
_ipv6 == rhs._ipv6 &&
_maxConnectionIdleTime == rhs._maxConnectionIdleTime &&
_maxConnectionLifeTime == rhs._maxConnectionLifeTime &&
@@ -795,6 +797,7 @@ public override int GetHashCode()
hash = 37 * hash + _credentialsStore.GetHashCode();
hash = 37 * hash + ((_defaultCredentials == null) ? 0 : _defaultCredentials.GetHashCode());
hash = 37 * hash + _guidRepresentation.GetHashCode();
+ hash = 37 * hash + ((_identity == null) ? 0 : _identity.GetHashCode());
hash = 37 * hash + _ipv6.GetHashCode();
hash = 37 * hash + _maxConnectionIdleTime.GetHashCode();
hash = 37 * hash + _maxConnectionLifeTime.GetHashCode();
@@ -833,6 +836,7 @@ public override string ToString()
sb.AppendFormat("Credentials={{{0}}};", _credentialsStore);
sb.AppendFormat("DefaultCredentials={0};", _defaultCredentials);
sb.AppendFormat("GuidRepresentation={0};", _guidRepresentation);
+ sb.AppendFormat("Identity={0};", _identity);
sb.AppendFormat("IPv6={0};", _ipv6);
sb.AppendFormat("MaxConnectionIdleTime={0};", _maxConnectionIdleTime);
sb.AppendFormat("MaxConnectionLifeTime={0};", _maxConnectionLifeTime);
View
10 MongoDB.Driver/MongoUrl.cs
@@ -65,6 +65,7 @@ public class MongoUrl : IEquatable<MongoUrl>
private readonly MongoCredentials _defaultCredentials;
private readonly bool? _fsync;
private readonly GuidRepresentation _guidRepresentation;
+ private readonly MongoClientIdentity _identity;
private readonly bool _ipv6;
private readonly bool? _journal;
private readonly TimeSpan _maxConnectionIdleTime;
@@ -100,6 +101,7 @@ public MongoUrl(string url)
_defaultCredentials = builder.DefaultCredentials;
_fsync = builder.FSync;
_guidRepresentation = builder.GuidRepresentation;
+ _identity = builder.Identity;
_ipv6 = builder.IPv6;
_journal = builder.Journal;
_maxConnectionIdleTime = builder.MaxConnectionIdleTime;
@@ -192,6 +194,14 @@ public GuidRepresentation GuidRepresentation
}
/// <summary>
+ /// Gets the identity.
+ /// </summary>
+ public MongoClientIdentity Identity
+ {
+ get { return _identity; }
+ }
+
+ /// <summary>
/// Gets whether to use IPv6.
/// </summary>
public bool IPv6
View
95 MongoDB.Driver/MongoUrlBuilder.cs
@@ -32,6 +32,8 @@ namespace MongoDB.Driver
[Serializable]
public class MongoUrlBuilder
{
+ private const string AUTH_TYPE_DEFAULT = "MONGO-CR";
+
// private fields
private ConnectionMode _connectionMode;
private TimeSpan _connectTimeout;
@@ -39,6 +41,7 @@ public class MongoUrlBuilder
private MongoCredentials _defaultCredentials;
private bool? _fsync;
private GuidRepresentation _guidRepresentation;
+ private MongoClientIdentity _identity;
private bool _ipv6;
private bool? _journal;
private TimeSpan _maxConnectionIdleTime;
@@ -71,6 +74,7 @@ public MongoUrlBuilder()
_defaultCredentials = null;
_fsync = null;
_guidRepresentation = MongoDefaults.GuidRepresentation;
+ _identity = null;
_ipv6 = false;
_journal = null;
_maxConnectionIdleTime = MongoDefaults.MaxConnectionIdleTime;
@@ -186,6 +190,15 @@ public GuidRepresentation GuidRepresentation
}
/// <summary>
+ /// Gets or sets the identity.
+ /// </summary>
+ public MongoClientIdentity Identity
+ {
+ get { return _identity; }
+ set { _identity = value; }
+ }
+
+ /// <summary>
/// Gets or sets whether to use IPv6.
/// </summary>
public bool IPv6
@@ -456,7 +469,7 @@ public WriteConcern.WValue W
get { return _w; }
set
{
- _w = value;
+ _w = value;
}
}
@@ -729,29 +742,30 @@ public WriteConcern GetWriteConcern(bool enabledDefault)
/// <param name="url">The URL.</param>
public void Parse(string url)
{
- const string serverPattern = @"((\[[^]]+?\]|[^:,/]+)(:\d+)?)";
+ const string serverPattern = @"((\[[^]]+?\]|[^:,/?#]+)(:\d+)?)";
const string pattern =
@"^mongodb://" +
- @"((?<username>[^:]+):(?<password>[^@]+)@)?" +
- @"(?<servers>" + serverPattern + "(," + serverPattern + ")*)?" +
+ @"((?<username>[^:]+)(:(?<password>.*?))?@)?" +
+ @"(?<servers>" + serverPattern + @"(," + serverPattern + ")*)?" +
@"(/(?<database>[^?]+)?(\?(?<query>.*))?)?$";
Match match = Regex.Match(url, pattern);
if (match.Success)
{
- string username = Uri.UnescapeDataString(match.Groups["username"].Value);
- string password = Uri.UnescapeDataString(match.Groups["password"].Value);
- string servers = match.Groups["servers"].Value;
- string databaseName = match.Groups["database"].Value;
- string query = match.Groups["query"].Value;
-
- if (username != "" && password != "")
+ string username = null;
+ string password = null;
+ var usernameGroup = match.Groups["username"];
+ if (usernameGroup.Success)
{
- _defaultCredentials = new MongoCredentials(username, password);
+ username = Uri.UnescapeDataString(usernameGroup.Value);
}
- else
+ var passwordGroup = match.Groups["password"];
+ if (passwordGroup.Success)
{
- _defaultCredentials = null;
+ password = Uri.UnescapeDataString(passwordGroup.Value);
}
+ string servers = match.Groups["servers"].Value;
+ string databaseName = match.Groups["database"].Value;
+ string query = match.Groups["query"].Value;
if (servers != "")
{
@@ -766,6 +780,7 @@ public void Parse(string url)
_databaseName = (databaseName != "") ? databaseName : null;
+ string authType = null;
if (!string.IsNullOrEmpty(query))
{
foreach (var pair in query.Split('&', ';'))
@@ -780,6 +795,9 @@ public void Parse(string url)
switch (name.ToLower())
{
+ case "authtype":
+ authType = value;
+ break;
case "connect":
ConnectionMode = ParseConnectionMode(name, value);
break;
@@ -891,6 +909,8 @@ public void Parse(string url)
}
}
}
+
+ HandleCredentials(username, password, authType);
}
else
{
@@ -898,6 +918,34 @@ public void Parse(string url)
}
}
+ private void HandleCredentials(string username, string password, string authType)
+ {
+ authType = authType ?? AUTH_TYPE_DEFAULT;
+ switch (authType.ToLower())
+ {
+ case "mongo-cr":
+ _defaultCredentials = null;
+ if (!string.IsNullOrEmpty(username))
+ {
+ _defaultCredentials = new MongoCredentials(username, password);
+ }
+ break;
+ case "gssapi":
+ if (!string.IsNullOrEmpty(username))
+ {
+ _identity = new MongoClientIdentity(username, password, MongoAuthenticationType.GSSAPI);
+ }
+ else
+ {
+ _identity = MongoClientIdentity.System;
+ }
+ break;
+ default:
+ var message = string.Format("{0} is not a valid authMechanism value. MONGO-CR and GSSAPI are the only valid authMechanism values.", authType);
+ throw new ArgumentException(message);
+ }
+ }
+
/// <summary>
/// Creates a new instance of MongoUrl based on the settings in this MongoUrlBuilder.
/// </summary>
@@ -929,6 +977,14 @@ public override string ToString()
{
url.AppendFormat("{0}:{1}@", Uri.EscapeDataString(_defaultCredentials.Username), Uri.EscapeDataString(_defaultCredentials.Password));
}
+ else if (_identity != null && _identity != MongoClientIdentity.System)
+ {
+ url.Append(Uri.EscapeDataString(_identity.Username));
+ if (_identity.HasPassword)
+ {
+ url.Append(":<password>");
+ }
+ }
if (_servers != null)
{
bool firstServer = true;
@@ -952,6 +1008,17 @@ public override string ToString()
url.Append(_databaseName);
}
var query = new StringBuilder();
+ if (_identity != null)
+ {
+ switch (_identity.AuthenticationType)
+ {
+ case MongoAuthenticationType.GSSAPI:
+ query.Append("authType=GSSAPI;");
+ break;
+ default:
+ throw new NotSupportedException(string.Format("Unknown MongoAuthenticationType {0}", _identity.AuthenticationType));
+ }
+ }
if (_ipv6)
{
query.AppendFormat("ipv6=true;");
View
35 MongoDB.DriverUnitTests/MongoUrlBuilderTests.cs
@@ -231,6 +231,7 @@ public void TestDefaultCredentials(string username, string password, string conn
{
Assert.AreEqual(defaultCredentials, builder.DefaultCredentials);
Assert.AreEqual(connectionString, builder.ToString());
+ Assert.AreEqual(null, builder.Identity);
}
}
@@ -249,6 +250,7 @@ public void TestDefaults()
Assert.AreEqual(null, builder.DefaultCredentials);
Assert.AreEqual(null, builder.FSync);
Assert.AreEqual(MongoDefaults.GuidRepresentation, builder.GuidRepresentation);
+ Assert.AreEqual(null, builder.Identity);
Assert.AreEqual(false, builder.IPv6);
Assert.AreEqual(null, builder.Journal);
Assert.AreEqual(MongoDefaults.MaxConnectionIdleTime, builder.MaxConnectionIdleTime);
@@ -394,6 +396,39 @@ public void TestGuidRepresentation(GuidRepresentation? guidRepresentation, strin
}
[Test]
+ [TestCase("user", null, MongoAuthenticationType.GSSAPI, "mongodb://user@localhost/?authType=GSSAPI")]
+ [TestCase("user", "", MongoAuthenticationType.GSSAPI, "mongodb://user:@localhost/?authType=GSSAPI")]
+ [TestCase("user", "pass", MongoAuthenticationType.GSSAPI, "mongodb://user:pass@localhost/?authType=GSSAPI")]
+ [TestCase("user@gmail.com", "pass", MongoAuthenticationType.GSSAPI, "mongodb://user@gmail.com:pass@localhost/?authType=GSSAPI")]
+ public void TestIdentity(string username, string password, MongoAuthenticationType authenticationType, string connectionString)
+ {
+ var identity = new MongoClientIdentity(username, password, authenticationType);
+ var built = new MongoUrlBuilder { Identity = identity };
+
+ foreach (var builder in EnumerateBuiltAndParsedBuilders(built, connectionString))
+ {
+ Assert.IsNotNull(builder.Identity);
+ Assert.AreEqual(identity.Username, builder.Identity.Username);
+ Assert.AreEqual(identity.Password, builder.Identity.Password);
+ Assert.AreEqual(identity.AuthenticationType, builder.Identity.AuthenticationType);
+ }
+ }
+
+ [Test]
+ [TestCase("mongodb://localhost/?authType=GSSAPI")]
+ public void TestIdentity_SystemGSSAPI(string connectionString)
+ {
+ var identity = MongoClientIdentity.System;
+ var built = new MongoUrlBuilder { Identity = MongoClientIdentity.System };
+
+ foreach (var builder in EnumerateBuiltAndParsedBuilders(built, connectionString))
+ {
+ Assert.IsNotNull(builder.Identity);
+ Assert.AreSame(identity, builder.Identity);
+ }
+ }
+
+ [Test]
[TestCase(null, "mongodb://localhost", new[] { "" })]
[TestCase(false, "mongodb://localhost/?ipv6={0}", new[] { "false", "False" })]
[TestCase(true, "mongodb://localhost/?ipv6={0}", new[] { "true", "True" })]

0 comments on commit d8fda44

Please sign in to comment.
Something went wrong with that request. Please try again.