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