PageRenderTime 25ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/BlogEngine/DotNetSlave.BusinessLogic/Providers/XmlProvider/XmlMembershipProvider.cs

#
C# | 867 lines | 480 code | 94 blank | 293 comment | 43 complexity | df5c833d6fdbeb866db43599d08c8bb3 MD5 | raw file
Possible License(s): LGPL-2.1, Apache-2.0, BSD-3-Clause
  1. namespace BlogEngine.Core.Providers
  2. {
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Collections.Specialized;
  6. using System.Configuration.Provider;
  7. using System.Globalization;
  8. using System.Linq;
  9. using System.Security.Permissions;
  10. using System.Web;
  11. using System.Web.Hosting;
  12. using System.Web.Security;
  13. using System.Web.Configuration;
  14. using System.Xml;
  15. using System.IO;
  16. /// <summary>
  17. /// The xml membership provider.
  18. /// </summary>
  19. public class XmlMembershipProvider : MembershipProvider
  20. {
  21. #region Constants and Fields
  22. /// <summary>
  23. /// The password format.
  24. /// </summary>
  25. private MembershipPasswordFormat passwordFormat;
  26. /// <summary>
  27. /// The users.
  28. /// </summary>
  29. private Dictionary<Guid, Dictionary<string, MembershipUser>> users;
  30. /// <summary>
  31. /// The xml file name.
  32. /// </summary>
  33. private string xmlFileName;
  34. #endregion
  35. // MembershipProvider Properties
  36. #region Properties
  37. private string XmlFullyQualifiedPath
  38. {
  39. get
  40. {
  41. string location = Blog.CurrentInstance.StorageLocation;
  42. string fullyQualifiedPath =
  43. HostingEnvironment.MapPath(
  44. VirtualPathUtility.Combine(
  45. VirtualPathUtility.AppendTrailingSlash(HttpRuntime.AppDomainAppVirtualPath), location + xmlFileName));
  46. return fullyQualifiedPath;
  47. }
  48. }
  49. /// <summary>
  50. /// The name of the application using the custom membership provider.
  51. /// </summary>
  52. /// <value></value>
  53. /// <returns>
  54. /// The name of the application using the custom membership provider.
  55. /// </returns>
  56. public override string ApplicationName
  57. {
  58. get
  59. {
  60. throw new NotSupportedException();
  61. }
  62. set
  63. {
  64. throw new NotSupportedException();
  65. }
  66. }
  67. /// <summary>
  68. /// Indicates whether the membership provider is configured to allow users to reset their passwords.
  69. /// </summary>
  70. /// <value></value>
  71. /// <returns>true if the membership provider supports password reset; otherwise, false. The default is true.
  72. /// </returns>
  73. public override bool EnablePasswordReset
  74. {
  75. get
  76. {
  77. return false;
  78. }
  79. }
  80. /// <summary>
  81. /// Indicates whether the membership provider is configured to allow users to retrieve their passwords.
  82. /// </summary>
  83. /// <value></value>
  84. /// <returns>true if the membership provider is configured to support password retrieval; otherwise, false. The default is false.
  85. /// </returns>
  86. public override bool EnablePasswordRetrieval
  87. {
  88. get
  89. {
  90. return false;
  91. }
  92. }
  93. /// <summary>
  94. /// Gets the number of invalid password or password-answer attempts allowed before the membership user is locked out.
  95. /// </summary>
  96. /// <value></value>
  97. /// <returns>
  98. /// The number of invalid password or password-answer attempts allowed before the membership user is locked out.
  99. /// </returns>
  100. public override int MaxInvalidPasswordAttempts
  101. {
  102. get
  103. {
  104. return 5;
  105. }
  106. }
  107. /// <summary>
  108. /// Gets the minimum number of special characters that must be present in a valid password.
  109. /// </summary>
  110. /// <value></value>
  111. /// <returns>
  112. /// The minimum number of special characters that must be present in a valid password.
  113. /// </returns>
  114. public override int MinRequiredNonAlphanumericCharacters
  115. {
  116. get
  117. {
  118. return 0;
  119. }
  120. }
  121. /// <summary>
  122. /// Gets the minimum length required for a password.
  123. /// </summary>
  124. /// <value></value>
  125. /// <returns>
  126. /// The minimum length required for a password.
  127. /// </returns>
  128. public override int MinRequiredPasswordLength
  129. {
  130. get
  131. {
  132. return 5;
  133. }
  134. }
  135. /// <summary>
  136. /// Gets the number of minutes in which a maximum number of invalid password or password-answer attempts are allowed before the membership user is locked out.
  137. /// </summary>
  138. /// <value></value>
  139. /// <returns>
  140. /// The number of minutes in which a maximum number of invalid password or password-answer attempts are allowed before the membership user is locked out.
  141. /// </returns>
  142. public override int PasswordAttemptWindow
  143. {
  144. get
  145. {
  146. throw new NotSupportedException();
  147. }
  148. }
  149. /// <summary>
  150. /// Gets a value indicating the format for storing passwords in the membership data store.
  151. /// </summary>
  152. /// <value></value>
  153. /// <returns>
  154. /// One of the <see cref="T:System.Web.Security.MembershipPasswordFormat"/> values indicating the format for storing passwords in the data store.
  155. /// </returns>
  156. public override MembershipPasswordFormat PasswordFormat
  157. {
  158. get
  159. {
  160. return this.passwordFormat;
  161. }
  162. }
  163. /// <summary>
  164. /// Gets the regular expression used to evaluate a password.
  165. /// </summary>
  166. /// <value></value>
  167. /// <returns>
  168. /// A regular expression used to evaluate a password.
  169. /// </returns>
  170. public override string PasswordStrengthRegularExpression
  171. {
  172. get
  173. {
  174. throw new NotSupportedException();
  175. }
  176. }
  177. /// <summary>
  178. /// Gets a value indicating whether the membership provider is configured to require the user to answer a password question for password reset and retrieval.
  179. /// </summary>
  180. /// <value></value>
  181. /// <returns>true if a password answer is required for password reset and retrieval; otherwise, false. The default is true.
  182. /// </returns>
  183. public override bool RequiresQuestionAndAnswer
  184. {
  185. get
  186. {
  187. return false;
  188. }
  189. }
  190. /// <summary>
  191. /// Gets a value indicating whether the membership provider is configured to require a unique e-mail address for each user name.
  192. /// </summary>
  193. /// <value></value>
  194. /// <returns>true if the membership provider requires a unique e-mail address; otherwise, false. The default is true.
  195. /// </returns>
  196. public override bool RequiresUniqueEmail
  197. {
  198. get
  199. {
  200. return false;
  201. }
  202. }
  203. #endregion
  204. #region Public Methods
  205. /// <summary>
  206. /// Processes a request to update the password for a membership user.
  207. /// </summary>
  208. /// <param name="username">The user to update the password for.</param>
  209. /// <param name="oldPassword">The current password for the specified user.</param>
  210. /// <param name="newPassword">The new password for the specified user.</param>
  211. /// <returns>
  212. /// true if the password was updated successfully; otherwise, false.
  213. /// </returns>
  214. public override bool ChangePassword(string username, string oldPassword, string newPassword)
  215. {
  216. var doc = new XmlDocument();
  217. doc.Load(XmlFullyQualifiedPath);
  218. var nodes = doc.GetElementsByTagName("User");
  219. foreach (XmlNode node in nodes)
  220. {
  221. if (!node["UserName"].InnerText.Equals(username, StringComparison.OrdinalIgnoreCase))
  222. {
  223. continue;
  224. }
  225. if (!this.CheckPassword(node["Password"].InnerText, oldPassword))
  226. {
  227. continue;
  228. }
  229. string passwordPrep = this.passwordFormat == MembershipPasswordFormat.Hashed ? Utils.HashPassword(newPassword) : newPassword;
  230. node["Password"].InnerText = passwordPrep;
  231. doc.Save(XmlFullyQualifiedPath);
  232. this.users = null;
  233. this.ReadMembershipDataStore();
  234. return true;
  235. }
  236. return false;
  237. }
  238. /// <summary>
  239. /// Processes a request to update the password question and answer for a membership user.
  240. /// </summary>
  241. /// <param name="username">The user to change the password question and answer for.</param>
  242. /// <param name="password">The password for the specified user.</param>
  243. /// <param name="newPasswordQuestion">The new password question for the specified user.</param>
  244. /// <param name="newPasswordAnswer">The new password answer for the specified user.</param>
  245. /// <returns>
  246. /// true if the password question and answer are updated successfully; otherwise, false.
  247. /// </returns>
  248. public override bool ChangePasswordQuestionAndAnswer(
  249. string username, string password, string newPasswordQuestion, string newPasswordAnswer)
  250. {
  251. throw new NotSupportedException();
  252. }
  253. /// <summary>
  254. /// Creates the user.
  255. /// </summary>
  256. /// <param name="username">The username.</param>
  257. /// <param name="password">The password.</param>
  258. /// <param name="email">The email.</param>
  259. /// <param name="passwordQuestion">The password question.</param>
  260. /// <param name="passwordAnswer">The password answer.</param>
  261. /// <param name="approved">if set to <c>true</c> [approved].</param>
  262. /// <param name="providerUserKey">The provider user key.</param>
  263. /// <param name="status">The status.</param>
  264. /// <returns>A Membership User.</returns>
  265. public override MembershipUser CreateUser(
  266. string username,
  267. string password,
  268. string email,
  269. string passwordQuestion,
  270. string passwordAnswer,
  271. bool approved,
  272. object providerUserKey,
  273. out MembershipCreateStatus status)
  274. {
  275. this.ReadMembershipDataStore();
  276. if (this.users[Blog.CurrentInstance.Id].ContainsKey(username))
  277. {
  278. throw new NotSupportedException("The username is already in use. Please choose another username.");
  279. }
  280. var doc = new XmlDocument();
  281. doc.Load(XmlFullyQualifiedPath);
  282. XmlNode xmlUserRoot = doc.CreateElement("User");
  283. XmlNode xmlUserName = doc.CreateElement("UserName");
  284. XmlNode xmlPassword = doc.CreateElement("Password");
  285. XmlNode xmlEmail = doc.CreateElement("Email");
  286. XmlNode xmlLastLoginTime = doc.CreateElement("LastLoginTime");
  287. xmlUserName.InnerText = username;
  288. string passwordPrep = this.passwordFormat == MembershipPasswordFormat.Hashed ? Utils.HashPassword(password) : password;
  289. xmlPassword.InnerText = passwordPrep;
  290. xmlEmail.InnerText = email;
  291. xmlLastLoginTime.InnerText = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
  292. xmlUserRoot.AppendChild(xmlUserName);
  293. xmlUserRoot.AppendChild(xmlPassword);
  294. xmlUserRoot.AppendChild(xmlEmail);
  295. xmlUserRoot.AppendChild(xmlLastLoginTime);
  296. doc.SelectSingleNode("Users").AppendChild(xmlUserRoot);
  297. doc.Save(XmlFullyQualifiedPath);
  298. status = MembershipCreateStatus.Success;
  299. var user = new MembershipUser(
  300. this.Name,
  301. username,
  302. username,
  303. email,
  304. passwordQuestion,
  305. passwordPrep,
  306. approved,
  307. false,
  308. DateTime.Now,
  309. DateTime.Now,
  310. DateTime.Now,
  311. DateTime.Now,
  312. DateTime.MaxValue);
  313. this.users[Blog.CurrentInstance.Id].Add(username, user);
  314. return user;
  315. }
  316. /// <summary>
  317. /// Removes a user from the membership data source.
  318. /// </summary>
  319. /// <param name="username">The name of the user to delete.</param>
  320. /// <param name="deleteAllRelatedData">true to delete data related to the user from the database; false to leave data related to the user in the database.</param>
  321. /// <returns>
  322. /// true if the user was successfully deleted; otherwise, false.
  323. /// </returns>
  324. public override bool DeleteUser(string username, bool deleteAllRelatedData)
  325. {
  326. this.ReadMembershipDataStore();
  327. var doc = new XmlDocument();
  328. doc.Load(XmlFullyQualifiedPath);
  329. foreach (XmlNode node in
  330. doc.GetElementsByTagName("User").Cast<XmlNode>().Where(node => node.ChildNodes[0].InnerText.Equals(username, StringComparison.OrdinalIgnoreCase)))
  331. {
  332. doc.SelectSingleNode("Users").RemoveChild(node);
  333. doc.Save(XmlFullyQualifiedPath);
  334. this.users[Blog.CurrentInstance.Id].Remove(username);
  335. return true;
  336. }
  337. return false;
  338. }
  339. /// <summary>
  340. /// Gets a collection of membership users where the e-mail address contains the specified e-mail address to match.
  341. /// </summary>
  342. /// <param name="emailToMatch">The e-mail address to search for.</param>
  343. /// <param name="pageIndex">The index of the page of results to return. <paramref name="pageIndex"/> is zero-based.</param>
  344. /// <param name="pageSize">The size of the page of results to return.</param>
  345. /// <param name="totalRecords">The total number of matched users.</param>
  346. /// <returns>
  347. /// A <see cref="T:System.Web.Security.MembershipUserCollection"/> collection that contains a page of <paramref name="pageSize"/><see cref="T:System.Web.Security.MembershipUser"/> objects beginning at the page specified by <paramref name="pageIndex"/>.
  348. /// </returns>
  349. public override MembershipUserCollection FindUsersByEmail(
  350. string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
  351. {
  352. throw new NotSupportedException();
  353. }
  354. /// <summary>
  355. /// Gets a collection of membership users where the user name contains the specified user name to match.
  356. /// </summary>
  357. /// <param name="usernameToMatch">The user name to search for.</param>
  358. /// <param name="pageIndex">The index of the page of results to return. <paramref name="pageIndex"/> is zero-based.</param>
  359. /// <param name="pageSize">The size of the page of results to return.</param>
  360. /// <param name="totalRecords">The total number of matched users.</param>
  361. /// <returns>
  362. /// A <see cref="T:System.Web.Security.MembershipUserCollection"/> collection that contains a page of <paramref name="pageSize"/><see cref="T:System.Web.Security.MembershipUser"/> objects beginning at the page specified by <paramref name="pageIndex"/>.
  363. /// </returns>
  364. public override MembershipUserCollection FindUsersByName(
  365. string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
  366. {
  367. throw new NotSupportedException();
  368. }
  369. /// <summary>
  370. /// Retrieves a collection of all the users.
  371. /// This implementation ignores pageIndex and pageSize,
  372. /// and it doesn't sort the MembershipUser objects returned.
  373. /// </summary>
  374. /// <param name="pageIndex">The page Index.</param>
  375. /// <param name="pageSize">The page Size.</param>
  376. /// <param name="totalRecords">The total Records.</param>
  377. /// <returns>
  378. /// A <see cref="T:System.Web.Security.MembershipUserCollection"/> collection that contains a page of <paramref name="pageSize"/><see cref="T:System.Web.Security.MembershipUser"/> objects beginning at the page specified by <paramref name="pageIndex"/>.
  379. /// </returns>
  380. public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
  381. {
  382. this.ReadMembershipDataStore();
  383. var membershipUserCollection = new MembershipUserCollection();
  384. foreach (var pair in this.users[Blog.CurrentInstance.Id])
  385. {
  386. membershipUserCollection.Add(pair.Value);
  387. }
  388. totalRecords = membershipUserCollection.Count;
  389. return membershipUserCollection;
  390. }
  391. /// <summary>
  392. /// The get number of users online.
  393. /// </summary>
  394. /// <returns>
  395. /// The get number of users online.
  396. /// </returns>
  397. public override int GetNumberOfUsersOnline()
  398. {
  399. throw new NotSupportedException();
  400. }
  401. /// <summary>
  402. /// Gets the password for the user with the given username.
  403. /// </summary>
  404. /// <param name="username">
  405. /// the given username
  406. /// </param>
  407. /// <returns>
  408. /// the password if user is found, null otherwise.
  409. /// </returns>
  410. public string GetPassword(string username)
  411. {
  412. throw new NotSupportedException();
  413. }
  414. /// <summary>
  415. /// Gets the password for the specified user name from the data source.
  416. /// </summary>
  417. /// <param name="username">
  418. /// The user to retrieve the password for.
  419. /// </param>
  420. /// <param name="answer">
  421. /// The password answer for the user.
  422. /// </param>
  423. /// <returns>
  424. /// The password for the specified user name.
  425. /// </returns>
  426. public override string GetPassword(string username, string answer)
  427. {
  428. throw new NotSupportedException();
  429. }
  430. /// <summary>
  431. /// Retrieves a user based on his/hers username.
  432. /// the userIsOnline parameter is ignored.
  433. /// </summary>
  434. /// <param name="username">
  435. /// The username.
  436. /// </param>
  437. /// <param name="userIsOnline">
  438. /// The user Is Online.
  439. /// </param>
  440. public override MembershipUser GetUser(string username, bool userIsOnline)
  441. {
  442. if (string.IsNullOrEmpty(username))
  443. {
  444. return null;
  445. }
  446. this.ReadMembershipDataStore();
  447. // Retrieve the user from the data source
  448. MembershipUser user;
  449. return this.users[Blog.CurrentInstance.Id].TryGetValue(username, out user) ? user : null;
  450. }
  451. /// <summary>
  452. /// Get a user based on the username parameter.
  453. /// the userIsOnline parameter is ignored.
  454. /// </summary>
  455. /// <param name="providerUserKey">
  456. /// The provider User Key.
  457. /// </param>
  458. /// <param name="userIsOnline">
  459. /// The user Is Online.
  460. /// </param>
  461. public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)
  462. {
  463. if (providerUserKey == null)
  464. {
  465. throw new ArgumentNullException("providerUserKey");
  466. }
  467. var doc = new XmlDocument();
  468. doc.Load(XmlFullyQualifiedPath);
  469. return (from XmlNode node in doc.SelectNodes("//User")
  470. where node.ChildNodes[0].InnerText.Equals(providerUserKey.ToString(), StringComparison.OrdinalIgnoreCase)
  471. let userName = node.ChildNodes[0].InnerText
  472. let password = node.ChildNodes[1].InnerText
  473. let email = node.ChildNodes[2].InnerText
  474. let lastLoginTime = DateTime.Parse(node.ChildNodes[3].InnerText, CultureInfo.InvariantCulture)
  475. select new MembershipUser(this.Name, providerUserKey.ToString(), providerUserKey, email, string.Empty, password, true, false, DateTime.Now, lastLoginTime, DateTime.Now, DateTime.Now, DateTime.MaxValue)).FirstOrDefault();
  476. }
  477. /// <summary>
  478. /// Retrieves a username based on a matching email.
  479. /// </summary>
  480. /// <param name="email">
  481. /// The email.
  482. /// </param>
  483. /// <returns>
  484. /// The get user name by email.
  485. /// </returns>
  486. public override string GetUserNameByEmail(string email)
  487. {
  488. if (email == null)
  489. {
  490. throw new ArgumentNullException("email");
  491. }
  492. var doc = new XmlDocument();
  493. doc.Load(XmlFullyQualifiedPath);
  494. return doc.GetElementsByTagName("User").Cast<XmlNode>().Where(
  495. node => node.ChildNodes[2].InnerText.Equals(email.Trim(), StringComparison.OrdinalIgnoreCase)).Select(
  496. node => node.ChildNodes[0].InnerText).FirstOrDefault();
  497. }
  498. /// <summary>
  499. /// Initializes the provider.
  500. /// </summary>
  501. /// <param name="name">The friendly name of the provider.</param>
  502. /// <param name="config">A collection of the name/value pairs representing the provider-specific attributes specified in the configuration for this provider.</param>
  503. /// <exception cref="T:System.ArgumentNullException">
  504. /// The name of the provider is null.
  505. /// </exception>
  506. /// <exception cref="T:System.ArgumentException">
  507. /// The name of the provider has a length of zero.
  508. /// </exception>
  509. /// <exception cref="T:System.InvalidOperationException">
  510. /// An attempt is made to call <see cref="M:System.Configuration.Provider.ProviderBase.Initialize(System.string,System.Collections.Specialized.NameValueCollection)"/> on a provider after the provider has already been initialized.
  511. /// </exception>
  512. public override void Initialize(string name, NameValueCollection config)
  513. {
  514. if (config == null)
  515. {
  516. throw new ArgumentNullException("config");
  517. }
  518. if (string.IsNullOrEmpty(name))
  519. {
  520. name = "XmlMembershipProvider";
  521. }
  522. if (string.IsNullOrEmpty(config["description"]))
  523. {
  524. config.Remove("description");
  525. config.Add("description", "XML membership provider");
  526. }
  527. base.Initialize(name, config);
  528. // Initialize _XmlFileName and make sure the path
  529. // is app-relative
  530. var filename = config["xmlFileName"];
  531. if (string.IsNullOrEmpty(filename))
  532. {
  533. filename = "users.xml";
  534. }
  535. else
  536. {
  537. if (!VirtualPathUtility.IsAppRelative(filename))
  538. {
  539. throw new ArgumentException("xmlFileName must be app-relative");
  540. }
  541. }
  542. xmlFileName = filename;
  543. config.Remove("xmlFileName");
  544. // Make sure we have permission to read the XML data source and
  545. // throw an exception if we don't
  546. var permission = new FileIOPermission(FileIOPermissionAccess.Write, this.XmlFullyQualifiedPath);
  547. permission.Demand();
  548. // Password Format
  549. if (config["passwordFormat"] == null)
  550. {
  551. config["passwordFormat"] = "Hashed";
  552. this.passwordFormat = MembershipPasswordFormat.Hashed;
  553. }
  554. else if (string.Compare(config["passwordFormat"], "clear", true) == 0)
  555. {
  556. this.passwordFormat = MembershipPasswordFormat.Clear;
  557. }
  558. else
  559. {
  560. this.passwordFormat = MembershipPasswordFormat.Hashed;
  561. }
  562. config.Remove("passwordFormat");
  563. // Throw an exception if unrecognized attributes remain
  564. if (config.Count > 0)
  565. {
  566. var attr = config.GetKey(0);
  567. if (!string.IsNullOrEmpty(attr))
  568. {
  569. throw new ProviderException(string.Format("Unrecognized attribute: {0}", attr));
  570. }
  571. }
  572. }
  573. /// <summary>
  574. /// Resets a user's password to a new, automatically generated password.
  575. /// </summary>
  576. /// <param name="username">The user to reset the password for.</param>
  577. /// <param name="answer">The password answer for the specified user.</param>
  578. /// <returns>The new password for the specified user.</returns>
  579. public override string ResetPassword(string username, string answer)
  580. {
  581. var doc = new XmlDocument();
  582. doc.Load(XmlFullyQualifiedPath);
  583. var nodes = doc.GetElementsByTagName("User");
  584. var newPassword = Utils.RandomPassword();
  585. foreach (var node in
  586. nodes.Cast<XmlNode>().Where(
  587. node =>
  588. node["UserName"].InnerText.Equals(username, StringComparison.OrdinalIgnoreCase) &&
  589. !string.IsNullOrEmpty(node["Password"].InnerText)))
  590. {
  591. string passwordPrep;
  592. if (this.passwordFormat == MembershipPasswordFormat.Hashed)
  593. {
  594. passwordPrep = Utils.HashPassword(newPassword);
  595. }
  596. else
  597. {
  598. passwordPrep = newPassword;
  599. }
  600. node["Password"].InnerText = passwordPrep;
  601. doc.Save(XmlFullyQualifiedPath);
  602. this.users = null;
  603. this.ReadMembershipDataStore();
  604. return newPassword;
  605. }
  606. return string.Empty;
  607. }
  608. /// <summary>
  609. /// Clears a lock so that the membership user can be validated.
  610. /// </summary>
  611. /// <param name="userName">The membership user whose lock status you want to clear.</param>
  612. /// <returns>
  613. /// true if the membership user was successfully unlocked; otherwise, false.
  614. /// </returns>
  615. public override bool UnlockUser(string userName)
  616. {
  617. throw new NotSupportedException();
  618. }
  619. /// <summary>
  620. /// Updates a user. The username will not be changed.
  621. /// </summary>
  622. /// <param name="user">
  623. /// The membership user.
  624. /// </param>
  625. public override void UpdateUser(MembershipUser user)
  626. {
  627. this.ReadMembershipDataStore();
  628. var doc = new XmlDocument();
  629. doc.Load(XmlFullyQualifiedPath);
  630. foreach (var node in
  631. doc.GetElementsByTagName("User").Cast<XmlNode>().Where(node => node.ChildNodes[0].InnerText.Equals(user.UserName, StringComparison.OrdinalIgnoreCase)))
  632. {
  633. if (user.Comment.Length > 30)
  634. {
  635. node.ChildNodes[1].InnerText = user.Comment;
  636. }
  637. node.ChildNodes[2].InnerText = user.Email;
  638. node.ChildNodes[3].InnerText = user.LastLoginDate.ToString(
  639. "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
  640. doc.Save(XmlFullyQualifiedPath);
  641. this.users[Blog.CurrentInstance.Id][user.UserName] = user;
  642. }
  643. }
  644. /// <summary>
  645. /// Returns true if the username and password match an exsisting user.
  646. /// </summary>
  647. /// <param name="username">
  648. /// The username.
  649. /// </param>
  650. /// <param name="password">
  651. /// The password.
  652. /// </param>
  653. /// <returns>
  654. /// The validate user.
  655. /// </returns>
  656. public override bool ValidateUser(string username, string password)
  657. {
  658. var validated = false;
  659. if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
  660. {
  661. return false;
  662. }
  663. try
  664. {
  665. this.ReadMembershipDataStore();
  666. // Validate the user name and password
  667. MembershipUser user;
  668. if (this.users[Blog.CurrentInstance.Id].TryGetValue(username, out user))
  669. {
  670. validated = this.CheckPassword(user.Comment, password);
  671. }
  672. return validated;
  673. }
  674. catch (Exception)
  675. {
  676. return validated;
  677. }
  678. }
  679. #endregion
  680. #region Methods
  681. /// <summary>
  682. /// The check password.
  683. /// </summary>
  684. /// <param name="storedPassword">
  685. /// The stored password.
  686. /// </param>
  687. /// <param name="inputPassword">
  688. /// The input password.
  689. /// </param>
  690. /// <returns>
  691. /// The checked password.
  692. /// </returns>
  693. private bool CheckPassword(string storedPassword, string inputPassword)
  694. {
  695. var validated = false;
  696. if (storedPassword == string.Empty)
  697. {
  698. // This is a special case used for resetting.
  699. if (string.Compare(inputPassword, "admin", true) == 0)
  700. {
  701. validated = true;
  702. }
  703. }
  704. else
  705. {
  706. if (this.passwordFormat == MembershipPasswordFormat.Hashed)
  707. {
  708. if (storedPassword == Utils.HashPassword(inputPassword))
  709. {
  710. validated = true;
  711. }
  712. }
  713. else if (inputPassword == storedPassword)
  714. {
  715. validated = true;
  716. }
  717. }
  718. return validated;
  719. }
  720. /// <summary>
  721. /// Builds the internal cache of users.
  722. /// </summary>
  723. private void ReadMembershipDataStore()
  724. {
  725. lock (this)
  726. {
  727. if (this.users == null)
  728. {
  729. this.users = new Dictionary<Guid, Dictionary<string, MembershipUser>>();
  730. }
  731. if (!this.users.ContainsKey(Blog.CurrentInstance.Id))
  732. {
  733. this.users[Blog.CurrentInstance.Id] = new Dictionary<string, MembershipUser>(16, StringComparer.OrdinalIgnoreCase);
  734. var doc = new XmlDocument();
  735. doc.Load(XmlFullyQualifiedPath);
  736. var nodes = doc.GetElementsByTagName("User");
  737. foreach (var user in
  738. nodes.Cast<XmlNode>().Select(
  739. node => new MembershipUser(
  740. this.Name,
  741. // Provider name
  742. node["UserName"].InnerText,
  743. // Username
  744. node["UserName"].InnerText,
  745. // providerUserKey
  746. node["Email"].InnerText,
  747. // Email
  748. string.Empty,
  749. // passwordQuestion
  750. node["Password"].InnerText,
  751. // Comment
  752. true,
  753. // approved
  754. false,
  755. // isLockedOut
  756. DateTime.Now,
  757. // creationDate
  758. DateTime.Parse(node["LastLoginTime"].InnerText, CultureInfo.InvariantCulture),
  759. // lastLoginDate
  760. DateTime.Now,
  761. // lastActivityDate
  762. DateTime.Now,
  763. // lastPasswordChangedDate
  764. new DateTime(1980, 1, 1))))
  765. {
  766. this.users[Blog.CurrentInstance.Id].Add(user.UserName, user);
  767. }
  768. }
  769. }
  770. }
  771. #endregion
  772. }
  773. }