PageRenderTime 54ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/Server/ThirdParty/MongoDBDriver/MongoDB.Driver/MongoCredential.cs

https://github.com/egametang/ET
C# | 541 lines | 320 code | 40 blank | 181 comment | 58 complexity | eb6a25243fe034087272c7af7507e6e7 MD5 | raw file
Possible License(s): MIT
  1. /* Copyright 2010-present MongoDB Inc.
  2. *
  3. * Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. */
  15. using System;
  16. using System.Collections.Generic;
  17. using System.Linq;
  18. using System.Runtime.InteropServices;
  19. using System.Security;
  20. using MongoDB.Driver.Core.Authentication;
  21. using MongoDB.Shared;
  22. namespace MongoDB.Driver
  23. {
  24. /// <summary>
  25. /// Credential to access a MongoDB database.
  26. /// </summary>
  27. #if NET452
  28. [Serializable]
  29. #endif
  30. public class MongoCredential : IEquatable<MongoCredential>
  31. {
  32. // private fields
  33. private readonly MongoIdentityEvidence _evidence;
  34. private readonly MongoIdentity _identity;
  35. private readonly string _mechanism;
  36. private readonly Dictionary<string, object> _mechanismProperties;
  37. // constructors
  38. /// <summary>
  39. /// Initializes a new instance of the <see cref="MongoCredential" /> class.
  40. /// </summary>
  41. /// <param name="mechanism">Mechanism to authenticate with.
  42. /// In .NET Standard, authenticating via SCRAM-SHA-256 may not work with non-ASCII passwords because SaslPrep is
  43. /// not fully implemented due to the lack of a string normalization function in .NET Standard 1.5.
  44. /// Normalizing the password into Unicode Normalization Form KC beforehand MAY help.
  45. /// SCRAM-SHA-1 is the recommended alternative for now.</param>
  46. /// <param name="identity">The identity.</param>
  47. /// <param name="evidence">The evidence.</param>
  48. public MongoCredential(string mechanism, MongoIdentity identity, MongoIdentityEvidence evidence)
  49. {
  50. if (identity == null)
  51. {
  52. throw new ArgumentNullException("identity");
  53. }
  54. if (evidence == null)
  55. {
  56. throw new ArgumentNullException("evidence");
  57. }
  58. _mechanism = mechanism;
  59. _identity = identity;
  60. _evidence = evidence;
  61. _mechanismProperties = new Dictionary<string, object>();
  62. }
  63. // public properties
  64. /// <summary>
  65. /// Gets the evidence.
  66. /// </summary>
  67. public MongoIdentityEvidence Evidence
  68. {
  69. get { return _evidence; }
  70. }
  71. /// <summary>
  72. /// Gets the identity.
  73. /// </summary>
  74. public MongoIdentity Identity
  75. {
  76. get { return _identity; }
  77. }
  78. /// <summary>
  79. /// Gets the mechanism to authenticate with.
  80. /// In .NET Standard, authenticating via SCRAM-SHA-256 may not work with non-ASCII passwords because SaslPrep is
  81. /// not fully implemented due to the lack of a string normalization function in .NET Standard 1.5.
  82. /// Normalizing the password into Unicode Normalization Form KC beforehand MAY help.
  83. /// SCRAM-SHA-1 is the recommended alternative for now.
  84. /// </summary>
  85. public string Mechanism
  86. {
  87. get { return _mechanism; }
  88. }
  89. /// <summary>
  90. /// Gets the password.
  91. /// </summary>
  92. [Obsolete("Use Evidence instead.")]
  93. public string Password
  94. {
  95. get
  96. {
  97. var passwordEvidence = _evidence as PasswordEvidence;
  98. if (passwordEvidence != null)
  99. {
  100. return MongoUtils.ToInsecureString(passwordEvidence.SecurePassword);
  101. }
  102. return null;
  103. }
  104. }
  105. /// <summary>
  106. /// Gets the source.
  107. /// </summary>
  108. public string Source
  109. {
  110. get { return _identity.Source; }
  111. }
  112. /// <summary>
  113. /// Gets the username.
  114. /// </summary>
  115. public string Username
  116. {
  117. get { return _identity.Username; }
  118. }
  119. // public operators
  120. /// <summary>
  121. /// Compares two MongoCredentials.
  122. /// </summary>
  123. /// <param name="lhs">The first MongoCredential.</param>
  124. /// <param name="rhs">The other MongoCredential.</param>
  125. /// <returns>True if the two MongoCredentials are equal (or both null).</returns>
  126. public static bool operator ==(MongoCredential lhs, MongoCredential rhs)
  127. {
  128. return object.Equals(lhs, rhs);
  129. }
  130. /// <summary>
  131. /// Compares two MongoCredentials.
  132. /// </summary>
  133. /// <param name="lhs">The first MongoCredential.</param>
  134. /// <param name="rhs">The other MongoCredential.</param>
  135. /// <returns>True if the two MongoCredentials are not equal (or one is null and the other is not).</returns>
  136. public static bool operator !=(MongoCredential lhs, MongoCredential rhs)
  137. {
  138. return !(lhs == rhs);
  139. }
  140. // public static methods
  141. /// <summary>
  142. /// Creates a default credential.
  143. /// In .NET Standard, authenticating via SCRAM-SHA-256 may not work with non-ASCII passwords because SaslPrep is
  144. /// not fully implemented due to the lack of a string normalization function in .NET Standard 1.5.
  145. /// Normalizing the password into Unicode Normalization Form KC beforehand MAY help.
  146. /// SCRAM-SHA-1 is the recommended alternative for now.
  147. /// </summary>
  148. /// <param name="databaseName">Name of the database.</param>
  149. /// <param name="username">The username.</param>
  150. /// <param name="password">The password.</param>
  151. /// <returns>A default credential.</returns>
  152. public static MongoCredential CreateCredential(string databaseName, string username, string password)
  153. {
  154. return FromComponents(null,
  155. databaseName,
  156. username,
  157. new PasswordEvidence(password));
  158. }
  159. /// <summary>
  160. /// Creates a default credential.
  161. /// Less secure when used in conjunction with SCRAM-SHA-256, due to the need to store the password in a managed
  162. /// string in order to SaslPrep it.
  163. /// In .NET Standard, authenticating via SCRAM-SHA-256 may not work with non-ASCII passwords because SaslPrep is
  164. /// not fully implemented due to the lack of a string normalization function in .NET Standard 1.5.
  165. /// Normalizing the password into Unicode Normalization Form KC beforehand MAY help.
  166. /// SCRAM-SHA-1 is the recommended alternative for now.
  167. /// </summary>
  168. /// <param name="databaseName">Name of the database.</param>
  169. /// <param name="username">The username.</param>
  170. /// <param name="password">The password.</param>
  171. /// <returns>A default credential.</returns>
  172. public static MongoCredential CreateCredential(string databaseName, string username, SecureString password)
  173. {
  174. return FromComponents(null,
  175. databaseName,
  176. username,
  177. new PasswordEvidence(password));
  178. }
  179. /// <summary>
  180. /// Creates a GSSAPI credential.
  181. /// </summary>
  182. /// <param name="username">The username.</param>
  183. /// <returns>A credential for GSSAPI.</returns>
  184. /// <remarks>This overload is used primarily on linux.</remarks>
  185. public static MongoCredential CreateGssapiCredential(string username)
  186. {
  187. return FromComponents("GSSAPI",
  188. "$external",
  189. username,
  190. new ExternalEvidence());
  191. }
  192. /// <summary>
  193. /// Creates a GSSAPI credential.
  194. /// </summary>
  195. /// <param name="username">The username.</param>
  196. /// <param name="password">The password.</param>
  197. /// <returns>A credential for GSSAPI.</returns>
  198. public static MongoCredential CreateGssapiCredential(string username, string password)
  199. {
  200. return FromComponents("GSSAPI",
  201. "$external",
  202. username,
  203. new PasswordEvidence(password));
  204. }
  205. /// <summary>
  206. /// Creates a GSSAPI credential.
  207. /// </summary>
  208. /// <param name="username">The username.</param>
  209. /// <param name="password">The password.</param>
  210. /// <returns>A credential for GSSAPI.</returns>
  211. public static MongoCredential CreateGssapiCredential(string username, SecureString password)
  212. {
  213. return FromComponents("GSSAPI",
  214. "$external",
  215. username,
  216. new PasswordEvidence(password));
  217. }
  218. /// <summary>
  219. /// Creates a credential used with MONGODB-CR.
  220. /// </summary>
  221. /// <param name="databaseName">Name of the database.</param>
  222. /// <param name="username">The username.</param>
  223. /// <param name="password">The password.</param>
  224. /// <returns>A credential for MONGODB-CR.</returns>
  225. [Obsolete("MONGODB-CR was replaced by SCRAM-SHA-1 in MongoDB 3.0, and is now deprecated.")]
  226. public static MongoCredential CreateMongoCRCredential(string databaseName, string username, string password)
  227. {
  228. return FromComponents("MONGODB-CR",
  229. databaseName,
  230. username,
  231. new PasswordEvidence(password));
  232. }
  233. /// <summary>
  234. /// Creates a credential used with MONGODB-CR.
  235. /// </summary>
  236. /// <param name="databaseName">Name of the database.</param>
  237. /// <param name="username">The username.</param>
  238. /// <param name="password">The password.</param>
  239. /// <returns>A credential for MONGODB-CR.</returns>
  240. [Obsolete("MONGODB-CR was replaced by SCRAM-SHA-1 in MongoDB 3.0, and is now deprecated.")]
  241. public static MongoCredential CreateMongoCRCredential(string databaseName, string username, SecureString password)
  242. {
  243. return FromComponents("MONGODB-CR",
  244. databaseName,
  245. username,
  246. new PasswordEvidence(password));
  247. }
  248. /// <summary>
  249. /// Creates a credential used with MONGODB-X509.
  250. /// </summary>
  251. /// <param name="username">The username.</param>
  252. /// <returns>A credential for MONGODB-X509.</returns>
  253. public static MongoCredential CreateMongoX509Credential(string username)
  254. {
  255. return FromComponents("MONGODB-X509",
  256. "$external",
  257. username,
  258. new ExternalEvidence());
  259. }
  260. /// <summary>
  261. /// Creates a PLAIN credential.
  262. /// </summary>
  263. /// <param name="databaseName">Name of the database.</param>
  264. /// <param name="username">The username.</param>
  265. /// <param name="password">The password.</param>
  266. /// <returns>A credential for PLAIN.</returns>
  267. public static MongoCredential CreatePlainCredential(string databaseName, string username, string password)
  268. {
  269. return FromComponents("PLAIN",
  270. databaseName,
  271. username,
  272. new PasswordEvidence(password));
  273. }
  274. /// <summary>
  275. /// Creates a PLAIN credential.
  276. /// </summary>
  277. /// <param name="databaseName">Name of the database.</param>
  278. /// <param name="username">The username.</param>
  279. /// <param name="password">The password.</param>
  280. /// <returns>A credential for PLAIN.</returns>
  281. public static MongoCredential CreatePlainCredential(string databaseName, string username, SecureString password)
  282. {
  283. return FromComponents("PLAIN",
  284. databaseName,
  285. username,
  286. new PasswordEvidence(password));
  287. }
  288. // public methods
  289. /// <summary>
  290. /// Gets the mechanism property.
  291. /// </summary>
  292. /// <typeparam name="T">The type of the mechanism property.</typeparam>
  293. /// <param name="key">The key.</param>
  294. /// <param name="defaultValue">The default value.</param>
  295. /// <returns>The mechanism property if one was set; otherwise the default value.</returns>
  296. public T GetMechanismProperty<T>(string key, T defaultValue)
  297. {
  298. object value;
  299. if (_mechanismProperties.TryGetValue(key, out value))
  300. {
  301. return (T)value;
  302. }
  303. return defaultValue;
  304. }
  305. /// <summary>
  306. /// Compares this MongoCredential to another MongoCredential.
  307. /// </summary>
  308. /// <param name="rhs">The other credential.</param>
  309. /// <returns>True if the two credentials are equal.</returns>
  310. public bool Equals(MongoCredential rhs)
  311. {
  312. if (object.ReferenceEquals(rhs, null) || GetType() != rhs.GetType()) { return false; }
  313. return _identity == rhs._identity &&
  314. _evidence == rhs._evidence &&
  315. _mechanism == rhs._mechanism &&
  316. _mechanismProperties.OrderBy(x => x.Key).SequenceEqual(rhs._mechanismProperties.OrderBy(x => x.Key));
  317. }
  318. /// <summary>
  319. /// Compares this MongoCredential to another MongoCredential.
  320. /// </summary>
  321. /// <param name="obj">The other credential.</param>
  322. /// <returns>True if the two credentials are equal.</returns>
  323. public override bool Equals(object obj)
  324. {
  325. return Equals(obj as MongoCredential); // works even if obj is null or of a different type
  326. }
  327. /// <summary>
  328. /// Gets the hashcode for the credential.
  329. /// </summary>
  330. /// <returns>The hashcode.</returns>
  331. public override int GetHashCode()
  332. {
  333. // see Effective Java by Joshua Bloch
  334. return new Hasher()
  335. .Hash(_identity)
  336. .Hash(_evidence)
  337. .Hash(_mechanism)
  338. .HashStructElements(_mechanismProperties)
  339. .GetHashCode();
  340. }
  341. /// <summary>
  342. /// Returns a string representation of the credential.
  343. /// </summary>
  344. /// <returns>A string representation of the credential.</returns>
  345. public override string ToString()
  346. {
  347. return string.Format("{0}@{1}", _identity.Username, _identity.Source);
  348. }
  349. /// <summary>
  350. /// Creates a new MongoCredential with the specified mechanism property.
  351. /// </summary>
  352. /// <param name="key">The key.</param>
  353. /// <param name="value">The value.</param>
  354. /// <returns>A new MongoCredential with the specified mechanism property.</returns>
  355. public MongoCredential WithMechanismProperty(string key, object value)
  356. {
  357. var copy = new MongoCredential(_mechanism, _identity, _evidence);
  358. foreach (var pair in _mechanismProperties)
  359. {
  360. copy._mechanismProperties.Add(pair.Key, pair.Value);
  361. }
  362. copy._mechanismProperties[key] = value; // overwrite if it's already set
  363. return copy;
  364. }
  365. // internal methods
  366. internal IAuthenticator ToAuthenticator()
  367. {
  368. var passwordEvidence = _evidence as PasswordEvidence;
  369. if (passwordEvidence != null)
  370. {
  371. var insecurePassword = MongoUtils.ToInsecureString(passwordEvidence.SecurePassword);
  372. var credential = new UsernamePasswordCredential(
  373. _identity.Source,
  374. _identity.Username,
  375. insecurePassword);
  376. if (_mechanism == null)
  377. {
  378. return new DefaultAuthenticator(credential);
  379. }
  380. #pragma warning disable 618
  381. else if (_mechanism == MongoDBCRAuthenticator.MechanismName)
  382. {
  383. return new MongoDBCRAuthenticator(credential);
  384. #pragma warning restore 618
  385. }
  386. else if (_mechanism == ScramSha1Authenticator.MechanismName)
  387. {
  388. return new ScramSha1Authenticator(credential);
  389. }
  390. else if (_mechanism == ScramSha256Authenticator.MechanismName)
  391. {
  392. return new ScramSha256Authenticator(credential);
  393. }
  394. else if (_mechanism == PlainAuthenticator.MechanismName)
  395. {
  396. return new PlainAuthenticator(credential);
  397. }
  398. else if (_mechanism == GssapiAuthenticator.MechanismName)
  399. {
  400. return new GssapiAuthenticator(
  401. credential,
  402. _mechanismProperties.Select(x => new KeyValuePair<string, string>(x.Key, x.Value.ToString())));
  403. }
  404. }
  405. else if (_identity.Source == "$external" && _evidence is ExternalEvidence)
  406. {
  407. if (_mechanism == MongoDBX509Authenticator.MechanismName)
  408. {
  409. return new MongoDBX509Authenticator(_identity.Username);
  410. }
  411. else if (_mechanism == GssapiAuthenticator.MechanismName)
  412. {
  413. return new GssapiAuthenticator(
  414. _identity.Username,
  415. _mechanismProperties.Select(x => new KeyValuePair<string, string>(x.Key, x.Value.ToString())));
  416. }
  417. }
  418. throw new NotSupportedException("Unable to create an authenticator.");
  419. }
  420. // internal static methods
  421. internal static MongoCredential FromComponents(string mechanism, string source, string username, string password)
  422. {
  423. var evidence = password == null ? (MongoIdentityEvidence)new ExternalEvidence() : new PasswordEvidence(password);
  424. return FromComponents(mechanism, source, username, evidence);
  425. }
  426. // private methods
  427. private void ValidatePassword(string password)
  428. {
  429. if (password == null)
  430. {
  431. throw new ArgumentNullException("password");
  432. }
  433. if (password.Any(c => (int)c >= 128))
  434. {
  435. throw new ArgumentException("Password must contain only ASCII characters.");
  436. }
  437. }
  438. // private static methods
  439. private static MongoCredential FromComponents(string mechanism, string source, string username, MongoIdentityEvidence evidence)
  440. {
  441. var defaultedMechanism = (mechanism ?? "DEFAULT").Trim().ToUpperInvariant();
  442. switch (defaultedMechanism)
  443. {
  444. case "DEFAULT":
  445. case "MONGODB-CR":
  446. case "SCRAM-SHA-1":
  447. case "SCRAM-SHA-256":
  448. // it is allowed for a password to be an empty string, but not a username
  449. source = source ?? "admin";
  450. if (evidence == null || !(evidence is PasswordEvidence))
  451. {
  452. var message = string.Format("A {0} credential must have a password.", defaultedMechanism);
  453. throw new ArgumentException(message);
  454. }
  455. return new MongoCredential(
  456. mechanism,
  457. new MongoInternalIdentity(source, username),
  458. evidence);
  459. case "MONGODB-X509":
  460. // always $external for X509.
  461. source = "$external";
  462. if (evidence == null || !(evidence is ExternalEvidence))
  463. {
  464. throw new ArgumentException("A MONGODB-X509 does not support a password.");
  465. }
  466. return new MongoCredential(
  467. mechanism,
  468. new MongoX509Identity(username),
  469. evidence);
  470. case "GSSAPI":
  471. // always $external for GSSAPI.
  472. source = "$external";
  473. return new MongoCredential(
  474. "GSSAPI",
  475. new MongoExternalIdentity(source, username),
  476. evidence);
  477. case "PLAIN":
  478. source = source ?? "admin";
  479. if (evidence == null || !(evidence is PasswordEvidence))
  480. {
  481. throw new ArgumentException("A PLAIN credential must have a password.");
  482. }
  483. MongoIdentity identity;
  484. if (source == "$external")
  485. {
  486. identity = new MongoExternalIdentity(source, username);
  487. }
  488. else
  489. {
  490. identity = new MongoInternalIdentity(source, username);
  491. }
  492. return new MongoCredential(
  493. mechanism,
  494. identity,
  495. evidence);
  496. default:
  497. throw new NotSupportedException(string.Format("Unsupported MongoAuthenticationMechanism {0}.", mechanism));
  498. }
  499. }
  500. }
  501. }