[System.Web.Extensions] Update default profile and remove old profiles files
[mono.git] / mcs / class / System.Web / System.Web.Security / SqlMembershipProvider.cs
1 //
2 // System.Web.Security.SqlMembershipProvider
3 //
4 // Authors:
5 //      Ben Maurer (bmaurer@users.sourceforge.net)
6 //      Lluis Sanchez Gual (lluis@novell.com)
7 //      Chris Toshok (toshok@ximian.com)
8 //
9 // (C) 2003 Ben Maurer
10 // Copyright (c) 2005,2006 Novell, Inc (http://www.novell.com)
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 using System.Collections;
33 using System.Collections.Specialized;
34 using System.Configuration;
35 using System.Configuration.Provider;
36 using System.Data;
37 using System.Data.Common;
38 using System.Text;
39 using System.Web.Configuration;
40 using System.Security.Cryptography;
41
42 namespace System.Web.Security {
43         public class SqlMembershipProvider : MembershipProvider
44         {
45                 bool enablePasswordReset;
46                 bool enablePasswordRetrieval;
47                 int maxInvalidPasswordAttempts;
48                 MembershipPasswordFormat passwordFormat;
49                 bool requiresQuestionAndAnswer;
50                 bool requiresUniqueEmail;
51                 int minRequiredNonAlphanumericCharacters;
52                 int minRequiredPasswordLength;
53                 int passwordAttemptWindow;
54                 string passwordStrengthRegularExpression;
55                 TimeSpan userIsOnlineTimeWindow;
56
57                 ConnectionStringSettings connectionString;
58                 DbProviderFactory factory;
59
60                 string applicationName;
61                 bool schemaIsOk = false;
62
63                 DbConnection CreateConnection ()
64                 {
65                         if (!schemaIsOk && !(schemaIsOk = AspNetDBSchemaChecker.CheckMembershipSchemaVersion (factory, connectionString.ConnectionString, "membership", "1")))
66                                 throw new ProviderException ("Incorrect ASP.NET DB Schema Version.");
67
68                         DbConnection connection;
69
70                         if (connectionString == null)
71                                 throw new ProviderException ("Connection string for the SQL Membership Provider has not been provided.");
72                         
73                         try {
74                                 connection = factory.CreateConnection ();
75                                 connection.ConnectionString = connectionString.ConnectionString;
76                                 connection.Open ();
77                         } catch (Exception ex) {
78                                 throw new ProviderException ("Unable to open SQL connection for the SQL Membership Provider.",
79                                                              ex);
80                         }
81                         
82                         return connection;
83                 }
84
85                 DbParameter AddParameter (DbCommand command, string parameterName, object parameterValue)
86                 {
87                         return AddParameter (command, parameterName, ParameterDirection.Input, parameterValue);
88                 }
89
90                 DbParameter AddParameter (DbCommand command, string parameterName, ParameterDirection direction, object parameterValue)
91                 {
92                         DbParameter dbp = command.CreateParameter ();
93                         dbp.ParameterName = parameterName;
94                         dbp.Value = parameterValue;
95                         dbp.Direction = direction;
96                         command.Parameters.Add (dbp);
97                         return dbp;
98                 }
99
100                 DbParameter AddParameter (DbCommand command, string parameterName, ParameterDirection direction, DbType type, object parameterValue)
101                 {
102                         DbParameter dbp = command.CreateParameter ();
103                         dbp.ParameterName = parameterName;
104                         dbp.Value = parameterValue;
105                         dbp.Direction = direction;
106                         dbp.DbType = type;
107                         command.Parameters.Add (dbp);
108                         return dbp;
109                 }
110
111                 static int GetReturnValue (DbParameter returnValue)
112                 {
113                         object value = returnValue.Value;
114                         return value is int ? (int) value : -1;
115                 }
116
117                 void CheckParam (string pName, string p, int length)
118                 {
119                         if (p == null)
120                                 throw new ArgumentNullException (pName);
121                         if (p.Length == 0 || p.Length > length || p.IndexOf (',') != -1)
122                                 throw new ArgumentException (String.Format ("invalid format for {0}", pName));
123                 }
124
125                 public override bool ChangePassword (string username, string oldPwd, string newPwd)
126                 {
127                         if (username != null) username = username.Trim ();
128                         if (oldPwd != null) oldPwd = oldPwd.Trim ();
129                         if (newPwd != null) newPwd = newPwd.Trim ();
130
131                         CheckParam ("username", username, 256);
132                         CheckParam ("oldPwd", oldPwd, 128);
133                         CheckParam ("newPwd", newPwd, 128);
134
135                         if (!CheckPassword (newPwd))
136                                 throw new ArgumentException (string.Format (
137                                                 "New Password invalid. New Password length minimum: {0}. Non-alphanumeric characters required: {1}.",
138                                                 MinRequiredPasswordLength,
139                                                 MinRequiredNonAlphanumericCharacters));
140
141                         using (DbConnection connection = CreateConnection ()) {
142                                 PasswordInfo pi = ValidateUsingPassword (username, oldPwd);
143
144                                 if (pi != null) {
145                                         EmitValidatingPassword (username, newPwd, false);
146                                         string db_password = EncodePassword (newPwd, pi.PasswordFormat, pi.PasswordSalt);
147
148                                         DbCommand command = factory.CreateCommand ();
149                                         command.Connection = connection;
150                                         command.CommandText = @"aspnet_Membership_SetPassword";
151                                         command.CommandType = CommandType.StoredProcedure;
152
153                                         AddParameter (command, "@ApplicationName", ApplicationName);
154                                         AddParameter (command, "@UserName", username);
155                                         AddParameter (command, "@NewPassword", db_password);
156                                         AddParameter (command, "@PasswordFormat", (int) pi.PasswordFormat);
157                                         AddParameter (command, "@PasswordSalt", pi.PasswordSalt);
158                                         AddParameter (command, "@CurrentTimeUtc", DateTime.UtcNow);
159                                         DbParameter returnValue = AddParameter (command, "@ReturnVal", ParameterDirection.ReturnValue, DbType.Int32, null);
160
161                                         command.ExecuteNonQuery ();
162
163                                         if (GetReturnValue (returnValue) != 0)
164                                                 return false;
165
166                                         return true;
167                                 }
168                                 return false;
169                         }
170                 }
171
172                 public override bool ChangePasswordQuestionAndAnswer (string username, string password, string newPwdQuestion, string newPwdAnswer)
173                 {
174                         if (username != null) username = username.Trim ();
175                         if (newPwdQuestion != null) newPwdQuestion = newPwdQuestion.Trim ();
176                         if (newPwdAnswer != null) newPwdAnswer = newPwdAnswer.Trim ();
177
178                         CheckParam ("username", username, 256);
179                         if (RequiresQuestionAndAnswer)
180                                 CheckParam ("newPwdQuestion", newPwdQuestion, 128);
181                         if (RequiresQuestionAndAnswer)
182                                 CheckParam ("newPwdAnswer", newPwdAnswer, 128);
183
184                         using (DbConnection connection = CreateConnection ()) {
185                                 PasswordInfo pi = ValidateUsingPassword (username, password);
186
187                                 if (pi != null) {
188                                         string db_passwordAnswer = EncodePassword (newPwdAnswer, pi.PasswordFormat, pi.PasswordSalt);
189
190                                         DbCommand command = factory.CreateCommand ();
191                                         command.Connection = connection;
192                                         command.CommandType = CommandType.StoredProcedure;
193                                         command.CommandText = @"aspnet_Membership_ChangePasswordQuestionAndAnswer";
194
195                                         AddParameter (command, "@ApplicationName", ApplicationName);
196                                         AddParameter (command, "@UserName", username);
197                                         AddParameter (command, "@NewPasswordQuestion", newPwdQuestion);
198                                         AddParameter (command, "@NewPasswordAnswer", db_passwordAnswer);
199                                         DbParameter returnValue = AddParameter (command, "@ReturnVal", ParameterDirection.ReturnValue, DbType.Int32, null);
200
201                                         command.ExecuteNonQuery ();
202
203                                         if (GetReturnValue (returnValue) != 0)
204                                                 return false;
205
206                                         return true;
207                                 }
208                                 return false;
209                         }
210                 }
211
212                 public override MembershipUser CreateUser (string username,
213                                                            string password,
214                                                            string email,
215                                                            string pwdQuestion,
216                                                            string pwdAnswer,
217                                                            bool isApproved,
218                                                            object providerUserKey,
219                                                            out MembershipCreateStatus status)
220                 {
221                         if (username != null) username = username.Trim ();
222                         if (password != null) password = password.Trim ();
223                         if (email != null) email = email.Trim ();
224                         if (pwdQuestion != null) pwdQuestion = pwdQuestion.Trim ();
225                         if (pwdAnswer != null) pwdAnswer = pwdAnswer.Trim ();
226
227                         /* some initial validation */
228                         if (username == null || username.Length == 0 || username.Length > 256 || username.IndexOf (',') != -1) {
229                                 status = MembershipCreateStatus.InvalidUserName;
230                                 return null;
231                         }
232                         if (password == null || password.Length == 0 || password.Length > 128) {
233                                 status = MembershipCreateStatus.InvalidPassword;
234                                 return null;
235                         }
236
237                         if (!CheckPassword (password)) {
238                                 status = MembershipCreateStatus.InvalidPassword;
239                                 return null;
240                         }
241                         EmitValidatingPassword (username, password, true);
242
243                         if (RequiresUniqueEmail && (email == null || email.Length == 0)) {
244                                 status = MembershipCreateStatus.InvalidEmail;
245                                 return null;
246                         }
247                         if (RequiresQuestionAndAnswer &&
248                                 (pwdQuestion == null ||
249                                  pwdQuestion.Length == 0 || pwdQuestion.Length > 256)) {
250                                 status = MembershipCreateStatus.InvalidQuestion;
251                                 return null;
252                         }
253                         if (RequiresQuestionAndAnswer &&
254                                 (pwdAnswer == null ||
255                                  pwdAnswer.Length == 0 || pwdAnswer.Length > 128)) {
256                                 status = MembershipCreateStatus.InvalidAnswer;
257                                 return null;
258                         }
259                         if (providerUserKey != null && !(providerUserKey is Guid)) {
260                                 status = MembershipCreateStatus.InvalidProviderUserKey;
261                                 return null;
262                         }
263
264                         if (providerUserKey == null)
265                                 providerUserKey = Guid.NewGuid();
266
267                         /* encode our password/answer using the
268                          * "passwordFormat" configuration option */
269                         string passwordSalt = "";
270
271                         RandomNumberGenerator rng = RandomNumberGenerator.Create ();
272                         byte [] salt = new byte [MembershipHelper.SALT_BYTES];
273                         rng.GetBytes (salt);
274                         passwordSalt = Convert.ToBase64String (salt);
275
276                         password = EncodePassword (password, PasswordFormat, passwordSalt);
277                         if (RequiresQuestionAndAnswer)
278                                 pwdAnswer = EncodePassword (pwdAnswer, PasswordFormat, passwordSalt);
279
280                         /* make sure the hashed/encrypted password and
281                          * answer are still under 128 characters. */
282                         if (password.Length > 128) {
283                                 status = MembershipCreateStatus.InvalidPassword;
284                                 return null;
285                         }
286
287                         if (RequiresQuestionAndAnswer) {
288                                 if (pwdAnswer.Length > 128) {
289                                         status = MembershipCreateStatus.InvalidAnswer;
290                                         return null;
291                                 }
292                         }
293                         status = MembershipCreateStatus.Success;
294
295                         using (DbConnection connection = CreateConnection ()) {
296
297                                 try {
298                                         DbCommand command = factory.CreateCommand ();
299                                         command.Connection = connection;
300                                         command.CommandText = @"aspnet_Membership_CreateUser";
301                                         command.CommandType = CommandType.StoredProcedure;
302
303                                         DateTime Now = DateTime.UtcNow;
304
305                                         AddParameter (command, "@ApplicationName", ApplicationName);
306                                         AddParameter (command, "@UserName", username);
307                                         AddParameter (command, "@Password", password);
308                                         AddParameter (command, "@PasswordSalt", passwordSalt);
309                                         AddParameter (command, "@Email", email);
310                                         AddParameter (command, "@PasswordQuestion", pwdQuestion);
311                                         AddParameter (command, "@PasswordAnswer", pwdAnswer);
312                                         AddParameter (command, "@IsApproved", isApproved);
313                                         AddParameter (command, "@CurrentTimeUtc", Now);
314                                         AddParameter (command, "@CreateDate", Now);
315                                         AddParameter (command, "@UniqueEmail", RequiresUniqueEmail);
316                                         AddParameter (command, "@PasswordFormat", (int) PasswordFormat);
317                                         AddParameter (command, "@UserId", ParameterDirection.InputOutput, providerUserKey);
318                                         DbParameter returnValue = AddParameter (command, "@ReturnVal", ParameterDirection.ReturnValue, DbType.Int32, null);
319
320                                         command.ExecuteNonQuery ();
321
322                                         int st = GetReturnValue (returnValue);
323
324                                         if (st == 0)
325                                                 return GetUser (username, false);
326                                         else if (st == 6)
327                                                 status = MembershipCreateStatus.DuplicateUserName;
328                                         else if (st == 7)
329                                                 status = MembershipCreateStatus.DuplicateEmail;
330                                         else if (st == 10)
331                                                 status = MembershipCreateStatus.DuplicateProviderUserKey;
332                                         else
333                                                 status = MembershipCreateStatus.ProviderError;
334
335                                         return null;
336                                 }
337                                 catch (Exception) {
338                                         status = MembershipCreateStatus.ProviderError;
339                                         return null;
340                                 }
341                         }
342                 }
343
344                 bool CheckPassword (string password)
345                 {
346                         if (password.Length < MinRequiredPasswordLength)
347                                 return false;
348
349                         if (MinRequiredNonAlphanumericCharacters > 0) {
350                                 int nonAlphanumeric = 0;
351                                 for (int i = 0; i < password.Length; i++) {
352                                         if (!Char.IsLetterOrDigit (password [i]))
353                                                 nonAlphanumeric++;
354                                 }
355                                 return nonAlphanumeric >= MinRequiredNonAlphanumericCharacters;
356                         }
357                         return true;
358                 }
359
360                 public override bool DeleteUser (string username, bool deleteAllRelatedData)
361                 {
362                         CheckParam ("username", username, 256);
363
364                         DeleteUserTableMask deleteBitmask = DeleteUserTableMask.MembershipUsers;
365
366                         if (deleteAllRelatedData)
367                                 deleteBitmask |=
368                                         DeleteUserTableMask.Profiles |
369                                         DeleteUserTableMask.UsersInRoles |
370                                         DeleteUserTableMask.WebPartStateUser;
371
372                         using (DbConnection connection = CreateConnection ()) {
373                                 DbCommand command = factory.CreateCommand ();
374                                 command.Connection = connection;
375                                 command.CommandText = @"aspnet_Users_DeleteUser";
376                                 command.CommandType = CommandType.StoredProcedure;
377
378                                 AddParameter (command, "@ApplicationName", ApplicationName);
379                                 AddParameter (command, "@UserName", username);
380                                 AddParameter (command, "@TablesToDeleteFrom", (int) deleteBitmask);
381                                 AddParameter (command, "@NumTablesDeletedFrom", ParameterDirection.Output, 0);
382                                 DbParameter returnValue = AddParameter (command, "@ReturnVal", ParameterDirection.ReturnValue, DbType.Int32, null);
383
384                                 command.ExecuteNonQuery ();
385
386                                 if (((int) command.Parameters ["@NumTablesDeletedFrom"].Value) == 0)
387                                         return false;
388
389                                 if (GetReturnValue (returnValue) == 0)
390                                         return true;
391
392                                 return false;
393                         }
394                 }
395
396                 public virtual string GeneratePassword ()
397                 {
398                         return Membership.GeneratePassword (MinRequiredPasswordLength, MinRequiredNonAlphanumericCharacters);
399                 }
400
401                 public override MembershipUserCollection FindUsersByEmail (string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
402                 {
403                         CheckParam ("emailToMatch", emailToMatch, 256);
404
405                         if (pageIndex < 0)
406                                 throw new ArgumentException ("pageIndex must be >= 0");
407                         if (pageSize < 0)
408                                 throw new ArgumentException ("pageSize must be >= 0");
409                         if (pageIndex * pageSize + pageSize - 1 > Int32.MaxValue)
410                                 throw new ArgumentException ("pageIndex and pageSize are too large");
411
412                         using (DbConnection connection = CreateConnection ()) {
413
414                                 DbCommand command = factory.CreateCommand ();
415                                 command.Connection = connection;
416                                 command.CommandText = @"aspnet_Membership_FindUsersByEmail";
417                                 command.CommandType = CommandType.StoredProcedure;
418
419                                 AddParameter (command, "@PageIndex", pageIndex);
420                                 AddParameter (command, "@PageSize", pageSize);
421                                 AddParameter (command, "@EmailToMatch", emailToMatch);
422                                 AddParameter (command, "@ApplicationName", ApplicationName);
423                                 // return value
424                                 AddParameter (command, "@ReturnValue", ParameterDirection.ReturnValue, null);
425
426                                 MembershipUserCollection c = BuildMembershipUserCollection (command, pageIndex, pageSize, out totalRecords);
427
428                                 return c;
429                         }
430                 }
431
432                 public override MembershipUserCollection FindUsersByName (string nameToMatch, int pageIndex, int pageSize, out int totalRecords)
433                 {
434                         CheckParam ("nameToMatch", nameToMatch, 256);
435
436                         if (pageIndex < 0)
437                                 throw new ArgumentException ("pageIndex must be >= 0");
438                         if (pageSize < 0)
439                                 throw new ArgumentException ("pageSize must be >= 0");
440                         if (pageIndex * pageSize + pageSize - 1 > Int32.MaxValue)
441                                 throw new ArgumentException ("pageIndex and pageSize are too large");
442
443                         using (DbConnection connection = CreateConnection ()) {
444
445                                 DbCommand command = factory.CreateCommand ();
446                                 command.Connection = connection;
447                                 command.CommandText = @"aspnet_Membership_FindUsersByName";
448                                 command.CommandType = CommandType.StoredProcedure;
449
450                                 AddParameter (command, "@PageIndex", pageIndex);
451                                 AddParameter (command, "@PageSize", pageSize);
452                                 AddParameter (command, "@UserNameToMatch", nameToMatch);
453                                 AddParameter (command, "@ApplicationName", ApplicationName);
454                                 // return value
455                                 AddParameter (command, "@ReturnValue", ParameterDirection.ReturnValue, null);
456
457                                 MembershipUserCollection c = BuildMembershipUserCollection (command, pageIndex, pageSize, out totalRecords);
458
459                                 return c;
460                         }
461                 }
462
463                 public override MembershipUserCollection GetAllUsers (int pageIndex, int pageSize, out int totalRecords)
464                 {
465                         if (pageIndex < 0)
466                                 throw new ArgumentException ("pageIndex must be >= 0");
467                         if (pageSize < 0)
468                                 throw new ArgumentException ("pageSize must be >= 0");
469                         if (pageIndex * pageSize + pageSize - 1 > Int32.MaxValue)
470                                 throw new ArgumentException ("pageIndex and pageSize are too large");
471
472                         using (DbConnection connection = CreateConnection ()) {
473                                 DbCommand command = factory.CreateCommand ();
474                                 command.Connection = connection;
475                                 command.CommandText = @"aspnet_Membership_GetAllUsers";
476                                 command.CommandType = CommandType.StoredProcedure;
477
478                                 AddParameter (command, "@ApplicationName", ApplicationName);
479                                 AddParameter (command, "@PageIndex", pageIndex);
480                                 AddParameter (command, "@PageSize", pageSize);
481                                 // return value
482                                 AddParameter (command, "@ReturnValue", ParameterDirection.ReturnValue, null);
483
484                                 MembershipUserCollection c = BuildMembershipUserCollection (command, pageIndex, pageSize, out totalRecords);
485
486                                 return c;
487                         }
488                 }
489
490                 MembershipUserCollection BuildMembershipUserCollection (DbCommand command, int pageIndex, int pageSize, out int totalRecords)
491                 {
492                         DbDataReader reader = null;
493                         try {
494                                 MembershipUserCollection users = new MembershipUserCollection ();
495                                 reader = command.ExecuteReader ();
496                                 while (reader.Read ())
497                                         users.Add (GetUserFromReader (reader, null, null));
498
499                                 totalRecords = Convert.ToInt32 (command.Parameters ["@ReturnValue"].Value);
500                                 return users;
501                         } catch (Exception) {
502                                 totalRecords = 0;
503                                 return null; /* should we let the exception through? */
504                         }
505                         finally {
506                                 if (reader != null)
507                                         reader.Close ();
508                         }
509                 }
510
511
512                 public override int GetNumberOfUsersOnline ()
513                 {
514                         using (DbConnection connection = CreateConnection ()) {
515                                 DateTime now = DateTime.UtcNow;
516
517                                 DbCommand command = factory.CreateCommand ();
518                                 command.Connection = connection;
519                                 command.CommandText = @"aspnet_Membership_GetNumberOfUsersOnline";
520                                 command.CommandType = CommandType.StoredProcedure;
521
522                                 AddParameter (command, "@CurrentTimeUtc", now.ToString ());
523                                 AddParameter (command, "@ApplicationName", ApplicationName);
524                                 AddParameter (command, "@MinutesSinceLastInActive", userIsOnlineTimeWindow.Minutes);
525                                 DbParameter returnValue = AddParameter (command, "@ReturnVal", ParameterDirection.ReturnValue, DbType.Int32, null);
526
527                                 command.ExecuteScalar ();
528                                 return GetReturnValue (returnValue);
529                         }
530                 }
531
532                 public override string GetPassword (string username, string answer)
533                 {
534                         if (!EnablePasswordRetrieval)
535                                 throw new NotSupportedException ("this provider has not been configured to allow the retrieval of passwords");
536
537                         CheckParam ("username", username, 256);
538                         if (RequiresQuestionAndAnswer)
539                                 CheckParam ("answer", answer, 128);
540
541                         PasswordInfo pi = GetPasswordInfo (username);
542                         if (pi == null)
543                                 throw new ProviderException ("An error occurred while retrieving the password from the database");
544
545                         string user_answer = EncodePassword (answer, pi.PasswordFormat, pi.PasswordSalt);
546                         string password = null;
547
548                         using (DbConnection connection = CreateConnection ()) {
549                                 DbCommand command = factory.CreateCommand ();
550                                 command.Connection = connection;
551                                 command.CommandText = @"aspnet_Membership_GetPassword";
552                                 command.CommandType = CommandType.StoredProcedure;
553
554                                 AddParameter (command, "@ApplicationName", ApplicationName);
555                                 AddParameter (command, "@UserName", username);
556                                 AddParameter (command, "@MaxInvalidPasswordAttempts", MaxInvalidPasswordAttempts);
557                                 AddParameter (command, "@PasswordAttemptWindow", PasswordAttemptWindow);
558                                 AddParameter (command, "@CurrentTimeUtc", DateTime.UtcNow);
559                                 AddParameter (command, "@PasswordAnswer", user_answer);
560                                 DbParameter retValue = AddParameter (command, "@ReturnVal", ParameterDirection.ReturnValue, DbType.Int32, null);
561
562                                 DbDataReader reader = command.ExecuteReader ();
563
564                                 int returnValue = GetReturnValue (retValue);
565                                 if (returnValue == 3)
566                                         throw new MembershipPasswordException ("Password Answer is invalid");
567                                 if (returnValue == 99)
568                                         throw new MembershipPasswordException ("The user account is currently locked out");
569
570                                 if (reader.Read ()) {
571                                         password = reader.GetString (0);
572                                         reader.Close ();
573                                 }
574
575                                 if (pi.PasswordFormat == MembershipPasswordFormat.Clear)
576                                         return password;
577                                 else if (pi.PasswordFormat == MembershipPasswordFormat.Encrypted)
578                                         return DecodePassword (password, pi.PasswordFormat);
579
580                                 return password;
581                         }
582                 }
583
584                 MembershipUser GetUserFromReader (DbDataReader reader, string username, object userId)
585                 {
586                         int i = 0;
587                         if (username == null)
588                                 i = 1;
589
590                         if (userId != null)
591                                 username = reader.GetString (8);
592
593                         return new MembershipUser (this.Name, /* XXX is this right?  */
594                                 (username == null ? reader.GetString (0) : username), /* name */
595                                 (userId == null ? reader.GetGuid (8 + i) : userId), /* providerUserKey */
596                                 reader.IsDBNull (0 + i) ? null : reader.GetString (0 + i), /* email */
597                                 reader.IsDBNull (1 + i) ? null : reader.GetString (1 + i), /* passwordQuestion */
598                                 reader.IsDBNull (2 + i) ? null : reader.GetString (2 + i), /* comment */
599                                 reader.GetBoolean (3 + i), /* isApproved */
600                                 reader.GetBoolean (9 + i), /* isLockedOut */
601                                 reader.GetDateTime (4 + i).ToLocalTime (), /* creationDate */
602                                 reader.GetDateTime (5 + i).ToLocalTime (), /* lastLoginDate */
603                                 reader.GetDateTime (6 + i).ToLocalTime (), /* lastActivityDate */
604                                 reader.GetDateTime (7 + i).ToLocalTime (), /* lastPasswordChangedDate */
605                                 reader.GetDateTime (10 + i).ToLocalTime () /* lastLockoutDate */);
606                 }
607
608                 MembershipUser BuildMembershipUser (DbCommand query, string username, object userId)
609                 {
610                         try {
611                                 using (DbConnection connection = CreateConnection ()) {
612                                         query.Connection = connection;
613                                         using (DbDataReader reader = query.ExecuteReader ()) {
614                                                 if (!reader.Read ())
615                                                         return null;
616
617                                                 return GetUserFromReader (reader, username, userId);
618                                         }
619                                 }
620                         } catch (Exception) {
621                                 return null; /* should we let the exception through? */
622                         }
623                         finally {
624                                 query.Connection = null;
625                         }
626                 }
627
628                 public override MembershipUser GetUser (string username, bool userIsOnline)
629                 {
630                         if (username == null)
631                                 throw new ArgumentNullException ("username");
632
633                         if (username.Length == 0)
634                                 return null;
635
636                         CheckParam ("username", username, 256);
637
638                         DbCommand command = factory.CreateCommand ();
639
640                         command.CommandText = @"aspnet_Membership_GetUserByName";
641                         command.CommandType = CommandType.StoredProcedure;
642
643                         AddParameter (command, "@UserName", username);
644                         AddParameter (command, "@ApplicationName", ApplicationName);
645                         AddParameter (command, "@CurrentTimeUtc", DateTime.Now);
646                         AddParameter (command, "@UpdateLastActivity", userIsOnline);
647
648                         MembershipUser u = BuildMembershipUser (command, username, null);
649
650                         return u;
651                 }
652
653                 public override MembershipUser GetUser (object providerUserKey, bool userIsOnline)
654                 {
655                         DbCommand command = factory.CreateCommand ();
656                         command.CommandText = @"aspnet_Membership_GetUserByUserId";
657                         command.CommandType = CommandType.StoredProcedure;
658
659                         AddParameter (command, "@UserId", providerUserKey);
660                         AddParameter (command, "@CurrentTimeUtc", DateTime.Now);
661                         AddParameter (command, "@UpdateLastActivity", userIsOnline);
662
663                         MembershipUser u = BuildMembershipUser (command, string.Empty, providerUserKey);
664                         return u;
665                 }
666
667                 public override string GetUserNameByEmail (string email)
668                 {
669                         CheckParam ("email", email, 256);
670
671                         using (DbConnection connection = CreateConnection ()) {
672
673                                 DbCommand command = factory.CreateCommand ();
674                                 command.Connection = connection;
675                                 command.CommandText = @"aspnet_Membership_GetUserByEmail";
676                                 command.CommandType = CommandType.StoredProcedure;
677
678                                 AddParameter (command, "@ApplicationName", ApplicationName);
679                                 AddParameter (command, "@Email", email);
680
681                                 DbDataReader reader = command.ExecuteReader ();
682                                 string rv = null;
683                                 if (reader.Read ())
684                                         rv = reader.GetString (0);
685                                 reader.Close ();
686                                 return rv;
687                         }
688                 }
689
690                 bool GetBoolConfigValue (NameValueCollection config, string name, bool def)
691                 {
692                         bool rv = def;
693                         string val = config [name];
694                         if (val != null) {
695                                 try { rv = Boolean.Parse (val); }
696                                 catch (Exception e) {
697                                         throw new ProviderException (String.Format ("{0} must be true or false", name), e);
698                                 }
699                         }
700                         return rv;
701                 }
702
703                 int GetIntConfigValue (NameValueCollection config, string name, int def)
704                 {
705                         int rv = def;
706                         string val = config [name];
707                         if (val != null) {
708                                 try { rv = Int32.Parse (val); }
709                                 catch (Exception e) {
710                                         throw new ProviderException (String.Format ("{0} must be an integer", name), e);
711                                 }
712                         }
713                         return rv;
714                 }
715
716                 int GetEnumConfigValue (NameValueCollection config, string name, Type enumType, int def)
717                 {
718                         int rv = def;
719                         string val = config [name];
720                         if (val != null) {
721                                 try { rv = (int) Enum.Parse (enumType, val); }
722                                 catch (Exception e) {
723                                         throw new ProviderException (String.Format ("{0} must be one of the following values: {1}", name, String.Join (",", Enum.GetNames (enumType))), e);
724                                 }
725                         }
726                         return rv;
727                 }
728
729                 string GetStringConfigValue (NameValueCollection config, string name, string def)
730                 {
731                         string rv = def;
732                         string val = config [name];
733                         if (val != null)
734                                 rv = val;
735                         return rv;
736                 }
737
738                 void EmitValidatingPassword (string username, string password, bool isNewUser)
739                 {
740                         ValidatePasswordEventArgs args = new ValidatePasswordEventArgs (username, password, isNewUser);
741                         OnValidatingPassword (args);
742
743                         /* if we're canceled.. */
744                         if (args.Cancel) {
745                                 if (args.FailureInformation == null)
746                                         throw new ProviderException ("Password validation canceled");
747                                 else
748                                         throw args.FailureInformation;
749                         }
750                 }
751
752                 public override void Initialize (string name, NameValueCollection config)
753                 {
754                         if (config == null)
755                                 throw new ArgumentNullException ("config");
756
757                         base.Initialize (name, config);
758
759                         applicationName = GetStringConfigValue (config, "applicationName", "/");
760                         enablePasswordReset = GetBoolConfigValue (config, "enablePasswordReset", true);
761                         enablePasswordRetrieval = GetBoolConfigValue (config, "enablePasswordRetrieval", false);
762                         requiresQuestionAndAnswer = GetBoolConfigValue (config, "requiresQuestionAndAnswer", true);
763                         requiresUniqueEmail = GetBoolConfigValue (config, "requiresUniqueEmail", false);
764                         passwordFormat = (MembershipPasswordFormat) GetEnumConfigValue (config, "passwordFormat", typeof (MembershipPasswordFormat),
765                                                                                            (int) MembershipPasswordFormat.Hashed);
766                         maxInvalidPasswordAttempts = GetIntConfigValue (config, "maxInvalidPasswordAttempts", 5);
767                         minRequiredPasswordLength = GetIntConfigValue (config, "minRequiredPasswordLength", 7);
768                         minRequiredNonAlphanumericCharacters = GetIntConfigValue (config, "minRequiredNonalphanumericCharacters", 1);
769                         passwordAttemptWindow = GetIntConfigValue (config, "passwordAttemptWindow", 10);
770                         passwordStrengthRegularExpression = GetStringConfigValue (config, "passwordStrengthRegularExpression", "");
771
772                         MembershipSection section = (MembershipSection) WebConfigurationManager.GetSection ("system.web/membership");
773
774                         userIsOnlineTimeWindow = section.UserIsOnlineTimeWindow;
775
776                         /* we can't support password retrieval with hashed passwords */
777                         if (passwordFormat == MembershipPasswordFormat.Hashed && enablePasswordRetrieval)
778                                 throw new ProviderException ("password retrieval cannot be used with hashed passwords");
779
780                         string connectionStringName = config ["connectionStringName"];
781
782                         if (applicationName.Length > 256)
783                                 throw new ProviderException ("The ApplicationName attribute must be 256 characters long or less.");
784                         if (connectionStringName == null || connectionStringName.Length == 0)
785                                 throw new ProviderException ("The ConnectionStringName attribute must be present and non-zero length.");
786
787                         connectionString = WebConfigurationManager.ConnectionStrings [connectionStringName];
788                         factory = connectionString == null || String.IsNullOrEmpty (connectionString.ProviderName) ?
789                                 System.Data.SqlClient.SqlClientFactory.Instance :
790                                 ProvidersHelper.GetDbProviderFactory (connectionString.ProviderName);
791                 }
792
793                 public override string ResetPassword (string username, string answer)
794                 {
795                         if (!EnablePasswordReset)
796                                 throw new NotSupportedException ("this provider has not been configured to allow the resetting of passwords");
797
798                         CheckParam ("username", username, 256);
799
800                         if (RequiresQuestionAndAnswer)
801                                 CheckParam ("answer", answer, 128);
802
803                         using (DbConnection connection = CreateConnection ()) {
804
805                                 PasswordInfo pi = GetPasswordInfo (username);
806                                 if (pi == null)
807                                         throw new ProviderException (username + "is not found in the membership database");
808
809                                 string newPassword = GeneratePassword ();
810                                 EmitValidatingPassword (username, newPassword, false);
811
812                                 string db_password = EncodePassword (newPassword, pi.PasswordFormat, pi.PasswordSalt);
813                                 string db_answer = EncodePassword (answer, pi.PasswordFormat, pi.PasswordSalt);
814
815                                 DbCommand command = factory.CreateCommand ();
816                                 command.Connection = connection;
817                                 command.CommandText = @"aspnet_Membership_ResetPassword";
818                                 command.CommandType = CommandType.StoredProcedure;
819
820                                 AddParameter (command, "@ApplicationName", ApplicationName);
821                                 AddParameter (command, "@UserName", username);
822                                 AddParameter (command, "@NewPassword", db_password);
823                                 AddParameter (command, "@MaxInvalidPasswordAttempts", MaxInvalidPasswordAttempts);
824                                 AddParameter (command, "@PasswordAttemptWindow", PasswordAttemptWindow);
825                                 AddParameter (command, "@PasswordSalt", pi.PasswordSalt);
826                                 AddParameter (command, "@CurrentTimeUtc", DateTime.UtcNow);
827                                 AddParameter (command, "@PasswordFormat", (int) pi.PasswordFormat);
828                                 AddParameter (command, "@PasswordAnswer", db_answer);
829                                 DbParameter retValue = AddParameter (command, "@ReturnVal", ParameterDirection.ReturnValue, DbType.Int32, null);
830
831                                 command.ExecuteNonQuery ();
832
833                                 int returnValue = GetReturnValue (retValue);
834
835                                 if (returnValue == 0)
836                                         return newPassword;
837                                 else if (returnValue == 3)
838                                         throw new MembershipPasswordException ("Password Answer is invalid");
839                                 else if (returnValue == 99)
840                                         throw new MembershipPasswordException ("The user account is currently locked out");
841                                 else
842                                         throw new ProviderException ("Failed to reset password");
843                         }
844                 }
845
846                 public override void UpdateUser (MembershipUser user)
847                 {
848                         if (user == null)
849                                 throw new ArgumentNullException ("user");
850
851                         if (user.UserName == null)
852                                 throw new ArgumentNullException ("user.UserName");
853
854                         if (RequiresUniqueEmail && user.Email == null)
855                                 throw new ArgumentNullException ("user.Email");
856
857                         CheckParam ("user.UserName", user.UserName, 256);
858
859                         if (user.Email.Length > 256 || (RequiresUniqueEmail && user.Email.Length == 0))
860                                 throw new ArgumentException ("invalid format for user.Email");
861
862                         using (DbConnection connection = CreateConnection ()) {
863                                 int returnValue = 0;
864
865                                 DbCommand command = factory.CreateCommand ();
866                                 command.Connection = connection;
867                                 command.CommandText = @"aspnet_Membership_UpdateUser";
868                                 command.CommandType = CommandType.StoredProcedure;
869
870                                 AddParameter (command, "@ApplicationName", ApplicationName);
871                                 AddParameter (command, "@UserName", user.UserName);
872                                 AddParameter (command, "@Email", user.Email == null ? (object) DBNull.Value : (object) user.Email);
873                                 AddParameter (command, "@Comment", user.Comment == null ? (object) DBNull.Value : (object) user.Comment);
874                                 AddParameter (command, "@IsApproved", user.IsApproved);
875                                 AddParameter (command, "@LastLoginDate", DateTime.UtcNow);
876                                 AddParameter (command, "@LastActivityDate", DateTime.UtcNow);
877                                 AddParameter (command, "@UniqueEmail", RequiresUniqueEmail);
878                                 AddParameter (command, "@CurrentTimeUtc", DateTime.UtcNow);
879                                 DbParameter retValue = AddParameter (command, "@ReturnVal", ParameterDirection.ReturnValue, DbType.Int32, null);
880
881                                 command.ExecuteNonQuery ();
882
883                                 returnValue = GetReturnValue (retValue);
884
885                                 if (returnValue == 1)
886                                         throw new ProviderException ("The UserName property of user was not found in the database.");
887                                 if (returnValue == 7)
888                                         throw new ProviderException ("The Email property of user was equal to an existing e-mail address in the database and RequiresUniqueEmail is set to true.");
889                                 if (returnValue != 0)
890                                         throw new ProviderException ("Failed to update user");
891                         }
892                 }
893
894                 public override bool ValidateUser (string username, string password)
895                 {
896                         if (username.Length == 0)
897                                 return false;
898
899                         CheckParam ("username", username, 256);
900                         EmitValidatingPassword (username, password, false);
901
902                         PasswordInfo pi = ValidateUsingPassword (username, password);
903                         if (pi != null) {
904                                 pi.LastLoginDate = DateTime.UtcNow;
905                                 UpdateUserInfo (username, pi, true, true);
906                                 return true;
907                         }
908                         return false;
909                 }
910
911
912                 public override bool UnlockUser (string username)
913                 {
914                         CheckParam ("username", username, 256);
915
916                         using (DbConnection connection = CreateConnection ()) {
917                                 try {
918                                         DbCommand command = factory.CreateCommand ();
919                                         command.Connection = connection;
920                                         command.CommandText = @"aspnet_Membership_UnlockUser"; ;
921                                         command.CommandType = CommandType.StoredProcedure;
922
923                                         AddParameter (command, "@ApplicationName", ApplicationName);
924                                         AddParameter (command, "@UserName", username);
925                                         DbParameter returnValue = AddParameter (command, "@ReturnVal", ParameterDirection.ReturnValue, DbType.Int32, null);
926
927                                         command.ExecuteNonQuery ();
928                                         if (GetReturnValue (returnValue) != 0)
929                                                 return false;
930                                 }
931                                 catch (Exception e) {
932                                         throw new ProviderException ("Failed to unlock user", e);
933                                 }
934                         }
935                         return true;
936                 }
937
938                 void UpdateUserInfo (string username, PasswordInfo pi, bool isPasswordCorrect, bool updateLoginActivity)
939                 {
940                         CheckParam ("username", username, 256);
941
942                         using (DbConnection connection = CreateConnection ()) {
943                                 try {
944                                         DbCommand command = factory.CreateCommand ();
945                                         command.Connection = connection;
946                                         command.CommandText = @"aspnet_Membership_UpdateUserInfo"; ;
947                                         command.CommandType = CommandType.StoredProcedure;
948
949                                         AddParameter (command, "@ApplicationName", ApplicationName);
950                                         AddParameter (command, "@UserName", username);
951                                         AddParameter (command, "@IsPasswordCorrect", isPasswordCorrect);
952                                         AddParameter (command, "@UpdateLastLoginActivityDate", updateLoginActivity);
953                                         AddParameter (command, "@MaxInvalidPasswordAttempts", MaxInvalidPasswordAttempts);
954                                         AddParameter (command, "@PasswordAttemptWindow", PasswordAttemptWindow);
955                                         AddParameter (command, "@CurrentTimeUtc", DateTime.UtcNow);
956                                         AddParameter (command, "@LastLoginDate", pi.LastLoginDate);
957                                         AddParameter (command, "@LastActivityDate", pi.LastActivityDate);
958                                         DbParameter retValue = AddParameter (command, "@ReturnVal", ParameterDirection.ReturnValue, DbType.Int32, null);
959
960                                         command.ExecuteNonQuery ();
961
962                                         int returnValue = GetReturnValue (retValue);
963                                         if (returnValue != 0)
964                                                 return;
965                                 }
966                                 catch (Exception e) {
967                                         throw new ProviderException ("Failed to update Membership table", e);
968                                 }
969
970                         }
971                 }
972
973                 PasswordInfo ValidateUsingPassword (string username, string password)
974                 {
975                         MembershipUser user = GetUser (username, true);
976                         if (user == null)
977                                 return null;
978
979                         if (!user.IsApproved || user.IsLockedOut)
980                                 return null;
981
982                         PasswordInfo pi = GetPasswordInfo (username);
983
984                         if (pi == null)
985                                 return null;
986
987                         /* do the actual validation */
988                         string user_password = EncodePassword (password, pi.PasswordFormat, pi.PasswordSalt);
989
990                         if (user_password != pi.Password) {
991                                 UpdateUserInfo (username, pi, false, false);
992                                 return null;
993                         }
994
995                         return pi;
996                 }
997
998                 PasswordInfo GetPasswordInfo (string username)
999                 {
1000                         using (DbConnection connection = CreateConnection ()) {
1001                                 DbCommand command = factory.CreateCommand ();
1002                                 command.Connection = connection;
1003                                 command.CommandType = CommandType.StoredProcedure;
1004                                 command.CommandText = @"aspnet_Membership_GetPasswordWithFormat";
1005
1006                                 AddParameter (command, "@ApplicationName", ApplicationName);
1007                                 AddParameter (command, "@UserName", username);
1008                                 AddParameter (command, "@UpdateLastLoginActivityDate", false);
1009                                 AddParameter (command, "@CurrentTimeUtc", DateTime.Now);
1010                                 // return value
1011                                 AddParameter (command, "@ReturnVal", ParameterDirection.ReturnValue, DbType.Int32, null);
1012
1013                                 DbDataReader reader = command.ExecuteReader ();
1014                                 if (!reader.Read ())
1015                                         return null;
1016
1017                                 PasswordInfo pi = new PasswordInfo (
1018                                         reader.GetString (0),
1019                                         (MembershipPasswordFormat) reader.GetInt32 (1),
1020                                         reader.GetString (2),
1021                                         reader.GetInt32 (3),
1022                                         reader.GetInt32 (4),
1023                                         reader.GetBoolean (5),
1024                                         reader.GetDateTime (6),
1025                                         reader.GetDateTime (7));
1026
1027                                 return pi;
1028                         }
1029                 }
1030
1031                 string EncodePassword (string password, MembershipPasswordFormat passwordFormat, string salt)
1032                 {
1033                         byte [] password_bytes;
1034                         byte [] salt_bytes;
1035
1036                         switch (passwordFormat) {
1037                                 case MembershipPasswordFormat.Clear:
1038                                         return password;
1039                                 case MembershipPasswordFormat.Hashed:
1040                                         password_bytes = Encoding.Unicode.GetBytes (password);
1041                                         salt_bytes = Convert.FromBase64String (salt);
1042
1043                                         byte [] hashBytes = new byte [salt_bytes.Length + password_bytes.Length];
1044
1045                                         Buffer.BlockCopy (salt_bytes, 0, hashBytes, 0, salt_bytes.Length);
1046                                         Buffer.BlockCopy (password_bytes, 0, hashBytes, salt_bytes.Length, password_bytes.Length);
1047
1048                                         MembershipSection section = (MembershipSection) WebConfigurationManager.GetSection ("system.web/membership");
1049                                         string alg_type = section.HashAlgorithmType;
1050                                         if (alg_type.Length == 0) {
1051                                                 alg_type = MachineKeySection.Config.Validation.ToString ();
1052                                                 // support new (4.0) custom algorithms
1053                                                 if (alg_type.StartsWith ("alg:"))
1054                                                         alg_type = alg_type.Substring (4);
1055                                         }
1056                                         using (HashAlgorithm hash = HashAlgorithm.Create (alg_type)) {
1057                                                 // for compatibility (with 2.0) we'll allow MD5 and SHA1 not to map to HMACMD5 and HMACSHA1
1058                                                 // but that won't work with new (4.0) algorithms, like HMACSHA256|384|512 or custom, won't work without using the key
1059                                                 KeyedHashAlgorithm kha = (hash as KeyedHashAlgorithm);
1060                                                 if (kha != null)
1061                                                         kha.Key = MachineKeySection.Config.GetValidationKey ();
1062                                                 hash.TransformFinalBlock (hashBytes, 0, hashBytes.Length);
1063                                                 return Convert.ToBase64String (hash.Hash);
1064                                         }
1065                                 case MembershipPasswordFormat.Encrypted:
1066                                         password_bytes = Encoding.Unicode.GetBytes (password);
1067                                         salt_bytes = Convert.FromBase64String (salt);
1068
1069                                         byte [] buf = new byte [password_bytes.Length + salt_bytes.Length];
1070
1071                                         Array.Copy (salt_bytes, 0, buf, 0, salt_bytes.Length);
1072                                         Array.Copy (password_bytes, 0, buf, salt_bytes.Length, password_bytes.Length);
1073
1074                                         return Convert.ToBase64String (EncryptPassword (buf));
1075                                 default:
1076                                         /* not reached.. */
1077                                         return null;
1078                         }
1079                 }
1080
1081                 string DecodePassword (string password, MembershipPasswordFormat passwordFormat)
1082                 {
1083                         switch (passwordFormat) {
1084                                 case MembershipPasswordFormat.Clear:
1085                                         return password;
1086                                 case MembershipPasswordFormat.Hashed:
1087                                         throw new ProviderException ("Hashed passwords cannot be decoded.");
1088                                 case MembershipPasswordFormat.Encrypted:
1089                                         return Encoding.Unicode.GetString (DecryptPassword (Convert.FromBase64String (password)));
1090                                 default:
1091                                         /* not reached.. */
1092                                         return null;
1093                         }
1094                 }
1095
1096                 public override string ApplicationName
1097                 {
1098                         get { return applicationName; }
1099                         set { applicationName = value; }
1100                 }
1101
1102                 public override bool EnablePasswordReset
1103                 {
1104                         get { return enablePasswordReset; }
1105                 }
1106
1107                 public override bool EnablePasswordRetrieval
1108                 {
1109                         get { return enablePasswordRetrieval; }
1110                 }
1111
1112                 public override MembershipPasswordFormat PasswordFormat
1113                 {
1114                         get { return passwordFormat; }
1115                 }
1116
1117                 public override bool RequiresQuestionAndAnswer
1118                 {
1119                         get { return requiresQuestionAndAnswer; }
1120                 }
1121
1122                 public override bool RequiresUniqueEmail
1123                 {
1124                         get { return requiresUniqueEmail; }
1125                 }
1126
1127                 public override int MaxInvalidPasswordAttempts
1128                 {
1129                         get { return maxInvalidPasswordAttempts; }
1130                 }
1131
1132                 public override int MinRequiredNonAlphanumericCharacters
1133                 {
1134                         get { return minRequiredNonAlphanumericCharacters; }
1135                 }
1136
1137                 public override int MinRequiredPasswordLength
1138                 {
1139                         get { return minRequiredPasswordLength; }
1140                 }
1141
1142                 public override int PasswordAttemptWindow
1143                 {
1144                         get { return passwordAttemptWindow; }
1145                 }
1146
1147                 public override string PasswordStrengthRegularExpression
1148                 {
1149                         get { return passwordStrengthRegularExpression; }
1150                 }
1151
1152                 [Flags]
1153                 enum DeleteUserTableMask
1154                 {
1155                         MembershipUsers = 1,
1156                         UsersInRoles = 2,
1157                         Profiles = 4,
1158                         WebPartStateUser = 8
1159                 }
1160
1161                 sealed class PasswordInfo
1162                 {
1163                         string _password;
1164                         MembershipPasswordFormat _passwordFormat;
1165                         string _passwordSalt;
1166                         int _failedPasswordAttemptCount;
1167                         int _failedPasswordAnswerAttemptCount;
1168                         bool _isApproved;
1169                         DateTime _lastLoginDate;
1170                         DateTime _lastActivityDate;
1171
1172                         internal PasswordInfo (
1173                                 string password,
1174                                 MembershipPasswordFormat passwordFormat,
1175                                 string passwordSalt,
1176                                 int failedPasswordAttemptCount,
1177                                 int failedPasswordAnswerAttemptCount,
1178                                 bool isApproved,
1179                                 DateTime lastLoginDate,
1180                                 DateTime lastActivityDate)
1181                         {
1182                                 _password = password;
1183                                 _passwordFormat = passwordFormat;
1184                                 _passwordSalt = passwordSalt;
1185                                 _failedPasswordAttemptCount = failedPasswordAttemptCount;
1186                                 _failedPasswordAnswerAttemptCount = failedPasswordAnswerAttemptCount;
1187                                 _isApproved = isApproved;
1188                                 _lastLoginDate = lastLoginDate;
1189                                 _lastActivityDate = lastActivityDate;
1190                         }
1191
1192                         public string Password
1193                         {
1194                                 get { return _password; }
1195                                 set { _password = value; }
1196                         }
1197                         public MembershipPasswordFormat PasswordFormat
1198                         {
1199                                 get { return _passwordFormat; }
1200                                 set { _passwordFormat = value; }
1201                         }
1202                         public string PasswordSalt
1203                         {
1204                                 get { return _passwordSalt; }
1205                                 set { _passwordSalt = value; }
1206                         }
1207                         public int FailedPasswordAttemptCount
1208                         {
1209                                 get { return _failedPasswordAttemptCount; }
1210                                 set { _failedPasswordAttemptCount = value; }
1211                         }
1212                         public int FailedPasswordAnswerAttemptCount
1213                         {
1214                                 get { return _failedPasswordAnswerAttemptCount; }
1215                                 set { _failedPasswordAnswerAttemptCount = value; }
1216                         }
1217                         public bool IsApproved
1218                         {
1219                                 get { return _isApproved; }
1220                                 set { _isApproved = value; }
1221                         }
1222                         public DateTime LastLoginDate
1223                         {
1224                                 get { return _lastLoginDate; }
1225                                 set { _lastLoginDate = value; }
1226                         }
1227                         public DateTime LastActivityDate
1228                         {
1229                                 get { return _lastActivityDate; }
1230                                 set { _lastActivityDate = value; }
1231                         }
1232                 }
1233         }
1234 }
1235