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