[Microsoft.Build] Fix expected output newline from ProcessWrapper.OutputStreamChanged...
[mono.git] / mcs / class / Mainsoft.Web / Mainsoft.Web.Security / DerbyMembershipProvider.cs
1 //\r
2 // Mainsoft.Web.Security.DerbyMembershipProvider\r
3 //\r
4 // Authors:\r
5 //      Ben Maurer (bmaurer@users.sourceforge.net)\r
6 //      Chris Toshok (toshok@ximian.com)\r
7 //      Vladimir Krasnov (vladimirk@mainsoft.com)\r
8 //\r
9 //\r
10 // Permission is hereby granted, free of charge, to any person obtaining\r
11 // a copy of this software and associated documentation files (the\r
12 // "Software"), to deal in the Software without restriction, including\r
13 // without limitation the rights to use, copy, modify, merge, publish,\r
14 // distribute, sublicense, and/or sell copies of the Software, and to\r
15 // permit persons to whom the Software is furnished to do so, subject to\r
16 // the following conditions:\r
17 // \r
18 // The above copyright notice and this permission notice shall be\r
19 // included in all copies or substantial portions of the Software.\r
20 // \r
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\r
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\r
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
28 //\r
29 \r
30
31 using System;
32 using System.Collections;
33 using System.Collections.Specialized;
34 using System.Configuration;
35 using System.Configuration.Provider;
36 using System.Data;\r
37 using System.Data.OleDb;
38 using System.Data.Common;
39 using System.Text;
40 using System.Web.Configuration;\r
41 using System.Security;
42 using System.Security.Cryptography;\r
43 using System.Web;\r
44 using System.Web.Security;\r
45 \r
46 namespace Mainsoft.Web.Security {
47         /// <summary>\r
48         /// <para>This class supports the Framework infrastructure and is not intended to be used directly from your code.</para>\r
49         /// <para>Manages storage of membership information for an ASP.NET application in a Derby database.</para>\r
50         /// </summary>
51         public class DerbyMembershipProvider : MembershipProvider
52         {
53                 const int SALT_BYTES = 16;
54
55                 bool enablePasswordReset;
56                 bool enablePasswordRetrieval;
57                 int maxInvalidPasswordAttempts;
58                 MembershipPasswordFormat passwordFormat;
59                 bool requiresQuestionAndAnswer;
60                 bool requiresUniqueEmail;
61                 int minRequiredNonAlphanumericCharacters;
62                 int minRequiredPasswordLength;
63                 int passwordAttemptWindow;
64                 string passwordStrengthRegularExpression;
65                 TimeSpan userIsOnlineTimeWindow;
66                 ConnectionStringSettings connectionString;
67                 bool schemaChecked = false;
68                 DerbyUnloadManager.DerbyShutDownPolicy shutDownPolicy = DerbyUnloadManager.DerbyShutDownPolicy.Default;
69
70                 string applicationName;
71
72                 DbConnection CreateConnection ()
73                 {\r
74                         if (!schemaChecked) {\r
75                                 DerbyDBSchema.CheckSchema (connectionString.ConnectionString);\r
76                                 schemaChecked = true;\r
77 \r
78                                 DerbyUnloadManager.RegisterUnloadHandler (connectionString.ConnectionString, shutDownPolicy);\r
79                         }\r
80 \r
81                         OleDbConnection connection = new OleDbConnection (connectionString.ConnectionString);
82                         connection.Open ();
83                         return connection;
84                 }
85
86                 void CheckParam (string pName, string p, int length)
87                 {
88                         if (p == null)
89                                 throw new ArgumentNullException (pName);
90                         if (p.Length == 0 || p.Length > length || p.IndexOf (",") != -1)
91                                 throw new ArgumentException (String.Format ("invalid format for {0}", pName));
92                 }
93
94                 public override bool ChangePassword (string username, string oldPwd, string newPwd)\r
95                 {\r
96                         if (username != null) username = username.Trim ();\r
97                         if (oldPwd != null) oldPwd = oldPwd.Trim ();\r
98                         if (newPwd != null) newPwd = newPwd.Trim ();\r
99 \r
100                         CheckParam ("username", username, 256);\r
101                         CheckParam ("oldPwd", oldPwd, 128);\r
102                         CheckParam ("newPwd", newPwd, 128);\r
103 \r
104                         if (!CheckPassword (newPwd))\r
105                                 throw new ArgumentException (string.Format (\r
106                                                 "New Password invalid. New Password length minimum: {0}. Non-alphanumeric characters required: {1}.",\r
107                                                 MinRequiredPasswordLength,\r
108                                                 MinRequiredNonAlphanumericCharacters));\r
109 \r
110                         using (DbConnection connection = CreateConnection ()) {\r
111                                 PasswordInfo pi = ValidateUsingPassword (username, oldPwd);\r
112 \r
113                                 if (pi != null) {\r
114                                         EmitValidatingPassword (username, newPwd, false);\r
115                                         string db_password = EncodePassword (newPwd, pi.PasswordFormat, pi.PasswordSalt);\r
116 \r
117                                         int st = DerbyMembershipHelper.Membership_SetPassword (connection, ApplicationName, username, db_password, (int) pi.PasswordFormat, pi.PasswordSalt, DateTime.UtcNow);\r
118 \r
119                                         if (st == 0)\r
120                                                 return true;\r
121                                 }\r
122                                 return false;\r
123                         }\r
124                 }
125
126                 public override bool ChangePasswordQuestionAndAnswer (string username, string password, string newPwdQuestion, string newPwdAnswer)
127                 {
128                         if (username != null) username = username.Trim ();
129                         if (newPwdQuestion != null) newPwdQuestion = newPwdQuestion.Trim ();
130                         if (newPwdAnswer != null) newPwdAnswer = newPwdAnswer.Trim ();
131
132                         CheckParam ("username", username, 256);
133                         if (RequiresQuestionAndAnswer)
134                                 CheckParam ("newPwdQuestion", newPwdQuestion, 128);
135                         if (RequiresQuestionAndAnswer)
136                                 CheckParam ("newPwdAnswer", newPwdAnswer, 128);
137
138                         using (DbConnection connection = CreateConnection ()) {
139                                 PasswordInfo pi = ValidateUsingPassword (username, password);
140
141                                 if (pi != null) {
142                                         string db_passwordAnswer = EncodePassword (newPwdAnswer, pi.PasswordFormat, pi.PasswordSalt);\r
143 \r
144                                         int st = DerbyMembershipHelper.Membership_ChangePasswordQuestionAndAnswer (connection, ApplicationName, username, newPwdQuestion, db_passwordAnswer);
145
146                                         if (st == 0)
147                                                 return true;
148                                 }
149                                 return false;
150                         }
151                 }
152
153                 public override MembershipUser CreateUser (string username,
154                                                            string password,
155                                                            string email,
156                                                            string pwdQuestion,
157                                                            string pwdAnswer,
158                                                            bool isApproved,
159                                                            object providerUserKey,
160                                                            out MembershipCreateStatus status)
161                 {
162                         if (username != null) username = username.Trim ();
163                         if (password != null) password = password.Trim ();
164                         if (email != null) email = email.Trim ();
165                         if (pwdQuestion != null) pwdQuestion = pwdQuestion.Trim ();
166                         if (pwdAnswer != null) pwdAnswer = pwdAnswer.Trim ();
167
168                         /* some initial validation */
169                         if (username == null || username.Length == 0 || username.Length > 256 || username.IndexOf (",") != -1) {
170                                 status = MembershipCreateStatus.InvalidUserName;
171                                 return null;
172                         }
173                         if (password == null || password.Length == 0 || password.Length > 128) {
174                                 status = MembershipCreateStatus.InvalidPassword;
175                                 return null;
176                         }
177
178                         if (!CheckPassword (password)) {
179                                 status = MembershipCreateStatus.InvalidPassword;
180                                 return null;
181                         }
182                         EmitValidatingPassword (username, password, true);
183
184                         if (RequiresUniqueEmail && (email == null || email.Length == 0)) {
185                                 status = MembershipCreateStatus.InvalidEmail;
186                                 return null;
187                         }
188                         if (RequiresQuestionAndAnswer &&
189                                 (pwdQuestion == null ||
190                                  pwdQuestion.Length == 0 || pwdQuestion.Length > 256)) {
191                                 status = MembershipCreateStatus.InvalidQuestion;
192                                 return null;
193                         }
194                         if (RequiresQuestionAndAnswer &&
195                                 (pwdAnswer == null ||
196                                  pwdAnswer.Length == 0 || pwdAnswer.Length > 128)) {
197                                 status = MembershipCreateStatus.InvalidAnswer;
198                                 return null;
199                         }
200                         if (providerUserKey != null && !(providerUserKey is Guid)) {
201                                 status = MembershipCreateStatus.InvalidProviderUserKey;
202                                 return null;
203                         }\r
204 \r
205                         /* encode our password/answer using the
206                          * "passwordFormat" configuration option */
207                         string passwordSalt = "";
208
209                         RandomNumberGenerator rng = RandomNumberGenerator.Create ();
210                         byte [] salt = new byte [SALT_BYTES];
211                         rng.GetBytes (salt);
212                         passwordSalt = Convert.ToBase64String (salt);
213
214                         password = EncodePassword (password, PasswordFormat, passwordSalt);
215                         if (RequiresQuestionAndAnswer)
216                                 pwdAnswer = EncodePassword (pwdAnswer, PasswordFormat, passwordSalt);
217
218                         /* make sure the hashed/encrypted password and
219                          * answer are still under 128 characters. */
220                         if (password.Length > 128) {
221                                 status = MembershipCreateStatus.InvalidPassword;
222                                 return null;
223                         }
224
225                         if (RequiresQuestionAndAnswer) {
226                                 if (pwdAnswer.Length > 128) {
227                                         status = MembershipCreateStatus.InvalidAnswer;
228                                         return null;
229                                 }
230                         }
231                         status = MembershipCreateStatus.Success;
232
233                         using (DbConnection connection = CreateConnection ()) {
234                                 try {\r
235 \r
236                                         object helperUserKey = providerUserKey != null ? providerUserKey.ToString () : null;\r
237                                         DateTime Now = DateTime.UtcNow;\r
238                                         int st = DerbyMembershipHelper.Membership_CreateUser (connection, ApplicationName, username, password, passwordSalt, email,\r
239                                                 pwdQuestion, pwdAnswer, isApproved, Now, Now, RequiresUniqueEmail, (int) PasswordFormat, ref helperUserKey);\r
240 \r
241                                         providerUserKey = new Guid ((string) helperUserKey);\r
242                                         if (st == 0)\r
243                                                 return GetUser (providerUserKey, false);
244                                         else if (st == 2)
245                                             status = MembershipCreateStatus.DuplicateUserName;
246                                         else if (st == 3)
247                                             status = MembershipCreateStatus.DuplicateEmail;
248                                         else if (st == 9)\r
249                                                 status = MembershipCreateStatus.InvalidProviderUserKey;
250                                         else if (st == 10)
251                                             status = MembershipCreateStatus.DuplicateProviderUserKey;
252                                         else
253                                             status = MembershipCreateStatus.ProviderError;\r
254 \r
255                                         return null;
256                                 }
257                                 catch (Exception) {\r
258                                         status = MembershipCreateStatus.ProviderError;
259                                         return null;
260                                 }
261                         }
262                 }
263
264                 private bool CheckPassword (string password)
265                 {
266                         if (password.Length < MinRequiredPasswordLength)
267                                 return false;
268
269                         if (MinRequiredNonAlphanumericCharacters > 0) {
270                                 int nonAlphanumeric = 0;
271                                 for (int i = 0; i < password.Length; i++) {
272                                         if (!Char.IsLetterOrDigit (password [i]))
273                                                 nonAlphanumeric++;
274                                 }
275                                 return nonAlphanumeric >= MinRequiredNonAlphanumericCharacters;
276                         }
277                         return true;
278                 }
279
280                 public override bool DeleteUser (string username, bool deleteAllRelatedData)
281                 {
282                         CheckParam ("username", username, 256);
283
284                         DeleteUserTableMask deleteBitmask = DeleteUserTableMask.MembershipUsers;
285
286                         if (deleteAllRelatedData)
287                                 deleteBitmask |=
288                                         DeleteUserTableMask.Profiles |
289                                         DeleteUserTableMask.UsersInRoles |
290                                         DeleteUserTableMask.WebPartStateUser;\r
291                         
292                         int num = 0;
293                         using (DbConnection connection = CreateConnection ()) {\r
294                                 int st = DerbyMembershipHelper.Users_DeleteUser (connection, ApplicationName, username, (int) deleteBitmask, ref num);\r
295 \r
296                                 if (num == 0)
297                                         return false;
298
299                                 if (st == 0)
300                                         return true;
301
302                                 return false;
303                         }
304                 }
305
306                 public virtual string GeneratePassword ()
307                 {
308                         return Membership.GeneratePassword (MinRequiredPasswordLength, MinRequiredNonAlphanumericCharacters);
309                 }
310
311                 public override MembershipUserCollection FindUsersByEmail (string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
312                 {
313                         CheckParam ("emailToMatch", emailToMatch, 256);
314
315                         if (pageIndex < 0)
316                                 throw new ArgumentException ("pageIndex must be >= 0");
317                         if (pageSize < 0)
318                                 throw new ArgumentException ("pageSize must be >= 0");
319                         if (pageIndex * pageSize + pageSize - 1 > Int32.MaxValue)
320                                 throw new ArgumentException ("pageIndex and pageSize are too large");\r
321 \r
322                         totalRecords = 0;
323                         using (DbConnection connection = CreateConnection ()) {\r
324                                 DbDataReader reader = null;\r
325 \r
326                                 DerbyMembershipHelper.Membership_FindUsersByEmail (connection, ApplicationName, emailToMatch, pageSize, pageIndex, out reader);\r
327                                 if (reader == null)\r
328                                         return null;\r
329
330                                 using (reader) {\r
331                                         return BuildMembershipUserCollection (reader, pageIndex, pageSize, out totalRecords);
332                                 }\r
333                         }
334                 }
335
336                 public override MembershipUserCollection FindUsersByName (string nameToMatch, int pageIndex, int pageSize, out int totalRecords)
337                 {
338                         CheckParam ("nameToMatch", nameToMatch, 256);
339
340                         if (pageIndex < 0)
341                                 throw new ArgumentException ("pageIndex must be >= 0");
342                         if (pageSize < 0)
343                                 throw new ArgumentException ("pageSize must be >= 0");
344                         if (pageIndex * pageSize + pageSize - 1 > Int32.MaxValue)
345                                 throw new ArgumentException ("pageIndex and pageSize are too large");\r
346 \r
347                         totalRecords = 0;
348                         using (DbConnection connection = CreateConnection ()) {\r
349                                 DbDataReader reader = null;\r
350 \r
351                                 DerbyMembershipHelper.Membership_FindUsersByName (connection, ApplicationName, nameToMatch, pageSize, pageIndex, out reader);\r
352                                 if (reader == null)\r
353                                         return null;\r
354 \r
355                                 using (reader) {\r
356                                         return BuildMembershipUserCollection (reader, pageIndex, pageSize, out totalRecords);\r
357                                 }\r
358                         }
359                 }
360
361                 public override MembershipUserCollection GetAllUsers (int pageIndex, int pageSize, out int totalRecords)
362                 {
363                         if (pageIndex < 0)
364                                 throw new ArgumentException ("pageIndex must be >= 0");
365                         if (pageSize < 0)
366                                 throw new ArgumentException ("pageSize must be >= 0");
367                         if (pageIndex * pageSize + pageSize - 1 > Int32.MaxValue)
368                                 throw new ArgumentException ("pageIndex and pageSize are too large");
369
370                         using (DbConnection connection = CreateConnection ()) {\r
371                                 DbDataReader reader = null;\r
372                                 totalRecords = DerbyMembershipHelper.Membership_GetAllUsers (connection, ApplicationName, pageIndex, pageSize, out reader);\r
373                                 return BuildMembershipUserCollection (reader, pageIndex, pageSize, out totalRecords);\r
374                         }
375                 }\r
376 \r
377                 MembershipUserCollection BuildMembershipUserCollection (DbDataReader reader, int pageIndex, int pageSize, out int totalRecords)
378                 {\r
379                         int num_read = 0;\r
380                         int num_added = 0;\r
381                         int num_to_skip = pageIndex * pageSize;\r
382                         MembershipUserCollection users = new MembershipUserCollection ();\r
383                         try {\r
384                                 while (reader.Read ()) {\r
385                                         if (num_read >= num_to_skip) {\r
386                                                 if (num_added < pageSize) {\r
387                                                         users.Add (GetUserFromReader (reader));\r
388                                                         num_added++;\r
389                                                 }\r
390                                         }\r
391                                         num_read++;\r
392                                 }\r
393                                 totalRecords = num_read;\r
394                                 return users;\r
395                         }\r
396                         catch (Exception) {\r
397                                 totalRecords = 0;\r
398                                 return null; /* should we let the exception through? */\r
399                         }
400                         finally {
401                                 if (reader != null)
402                                         reader.Close ();
403                         }
404                 }
405
406                 public override int GetNumberOfUsersOnline ()
407                 {
408                         using (DbConnection connection = CreateConnection ()) {
409                                 return DerbyMembershipHelper.Membership_GetNumberOfUsersOnline (connection, ApplicationName, userIsOnlineTimeWindow.Minutes, DateTime.UtcNow);\r
410                         }
411                 }
412
413                 public override string GetPassword (string username, string answer)
414                 {
415                         if (!EnablePasswordRetrieval)
416                                 throw new NotSupportedException ("this provider has not been configured to allow the retrieval of passwords");
417
418                         CheckParam ("username", username, 256);
419                         if (RequiresQuestionAndAnswer)
420                                 CheckParam ("answer", answer, 128);
421
422                         PasswordInfo pi = GetPasswordInfo (username);
423                         if (pi == null)
424                                 throw new ProviderException ("An error occurred while retrieving the password from the database");
425
426                         string user_answer = EncodePassword (answer, pi.PasswordFormat, pi.PasswordSalt);
427                         string password = null;
428
429                         using (DbConnection connection = CreateConnection ()) {\r
430                                 int st = DerbyMembershipHelper.Membership_GetPassword (connection, ApplicationName, username, user_answer, MaxInvalidPasswordAttempts, PasswordAttemptWindow, DateTime.UtcNow, out password);\r
431 \r
432                                 if (st == 1)\r
433                                         throw new ProviderException ("User specified by username is not found in the membership database");\r
434 \r
435                                 if (st == 2)\r
436                                         throw new MembershipPasswordException ("The membership user identified by username is locked out");\r
437                                 
438                                 if (st == 3)\r
439                                         throw new MembershipPasswordException ("Password Answer is invalid");
440
441                                 return DecodePassword (password, pi.PasswordFormat);
442                         }
443                 }\r
444 \r
445                 MembershipUser GetUserFromReader (DbDataReader reader)\r
446                 {\r
447                         return new MembershipUser (\r
448                                 this.Name,                                          /* XXX is this right?  */\r
449                                 reader.GetString (0),                               /* name */\r
450                                 new Guid (reader.GetString (1)),                    /* providerUserKey */\r
451                                 reader.IsDBNull (2) ? null : reader.GetString (2),  /* email */\r
452                                 reader.IsDBNull (3) ? null : reader.GetString (3),  /* passwordQuestion */\r
453                                 reader.IsDBNull (4) ? null : reader.GetString (4),  /* comment */\r
454                                 reader.GetInt32 (5) > 0,                            /* isApproved */\r
455                                 reader.GetInt32 (6) > 0,                            /* isLockedOut */\r
456                                 reader.GetDateTime (7).ToLocalTime (),              /* creationDate */\r
457                                 reader.GetDateTime (8).ToLocalTime (),              /* lastLoginDate */\r
458                                 reader.GetDateTime (9).ToLocalTime (),              /* lastActivityDate */\r
459                                 reader.GetDateTime (10).ToLocalTime (),             /* lastPasswordChangedDate */\r
460                                 reader.GetDateTime (11).ToLocalTime ()              /* lastLockoutDate */);\r
461                 }
462
463                 public override MembershipUser GetUser (string username, bool userIsOnline)
464                 {
465                         if (username.Length == 0)
466                                 return null;
467
468                         CheckParam ("username", username, 256);\r
469 \r
470                         using (DbConnection connection = CreateConnection ()) {\r
471                                 DbDataReader reader = null;\r
472                                 int st = DerbyMembershipHelper.Membership_GetUserByName (connection, ApplicationName, username, userIsOnline, DateTime.UtcNow, out reader);\r
473                                 using (reader) {\r
474                                         if (st == 0 && reader != null) {\r
475                                                 MembershipUser u = GetUserFromReader (reader);\r
476                                                 return u;\r
477                                         }\r
478                                 }\r
479                         }\r
480                         return null;\r
481                 }
482
483                 public override MembershipUser GetUser (object providerUserKey, bool userIsOnline)
484                 {\r
485                         if (providerUserKey == null)\r
486                                 throw new ArgumentNullException ("providerUserKey");\r
487 \r
488                         if (!(providerUserKey is Guid))\r
489                                 throw new ArgumentException ("providerUserKey is not of type Guid", "providerUserKey");\r
490 \r
491                         using (DbConnection connection = CreateConnection ()) {\r
492                                 DbDataReader reader = null;\r
493                                 int st = DerbyMembershipHelper.Membership_GetUserByUserId (connection, providerUserKey.ToString (), userIsOnline, DateTime.UtcNow, out reader);\r
494                                 using (reader) {\r
495                                         if (st == 0 && reader != null) {\r
496                                                 MembershipUser u = GetUserFromReader (reader);\r
497                                                 return u;\r
498                                         }\r
499                                 }\r
500                         }\r
501                         return null;
502                 }
503
504                 public override string GetUserNameByEmail (string email)
505                 {
506                         CheckParam ("email", email, 256);\r
507 \r
508                         string username = null;
509
510                         using (DbConnection connection = CreateConnection ()) {\r
511                                 int st = DerbyMembershipHelper.Membership_GetUserByEmail (connection, ApplicationName, email, out username);\r
512 \r
513                                 if (st == 1)\r
514                                         return null;\r
515                                 \r
516                                 if (st == 2 && RequiresUniqueEmail)\r
517                                         throw new ProviderException ("More than one user with the same e-mail address exists in the database and RequiresUniqueEmail is true");\r
518                         }\r
519                         return username;
520                 }
521
522                 bool GetBoolConfigValue (NameValueCollection config, string name, bool def)
523                 {
524                         bool rv = def;
525                         string val = config [name];
526                         if (val != null) {
527                                 try { rv = Boolean.Parse (val); }
528                                 catch (Exception e) {
529                                         throw new ProviderException (String.Format ("{0} must be true or false", name), e);
530                                 }
531                         }
532                         return rv;
533                 }
534
535                 int GetIntConfigValue (NameValueCollection config, string name, int def)
536                 {
537                         int rv = def;
538                         string val = config [name];
539                         if (val != null) {
540                                 try { rv = Int32.Parse (val); }
541                                 catch (Exception e) {
542                                         throw new ProviderException (String.Format ("{0} must be an integer", name), e);
543                                 }
544                         }
545                         return rv;
546                 }
547
548                 int GetEnumConfigValue (NameValueCollection config, string name, Type enumType, int def)
549                 {
550                         int rv = def;
551                         string val = config [name];
552                         if (val != null) {
553                                 try { rv = (int) Enum.Parse (enumType, val); }
554                                 catch (Exception e) {
555                                         throw new ProviderException (String.Format ("{0} must be one of the following values: {1}", name, String.Join (",", Enum.GetNames (enumType))), e);
556                                 }
557                         }
558                         return rv;
559                 }
560
561                 string GetStringConfigValue (NameValueCollection config, string name, string def)
562                 {
563                         string rv = def;
564                         string val = config [name];
565                         if (val != null)
566                                 rv = val;
567                         return rv;
568                 }
569
570                 void EmitValidatingPassword (string username, string password, bool isNewUser)
571                 {
572                         ValidatePasswordEventArgs args = new ValidatePasswordEventArgs (username, password, isNewUser);
573                         OnValidatingPassword (args);
574
575                         /* if we're canceled.. */
576                         if (args.Cancel) {
577                                 if (args.FailureInformation == null)
578                                         throw new ProviderException ("Password validation canceled");
579                                 else
580                                         throw args.FailureInformation;
581                         }
582                 }
583
584                 public override void Initialize (string name, NameValueCollection config)
585                 {
586                         if (config == null)
587                                 throw new ArgumentNullException ("config");
588
589                         base.Initialize (name, config);
590
591                         applicationName = GetStringConfigValue (config, "applicationName", "/");
592                         enablePasswordReset = GetBoolConfigValue (config, "enablePasswordReset", true);
593                         enablePasswordRetrieval = GetBoolConfigValue (config, "enablePasswordRetrieval", false);
594                         requiresQuestionAndAnswer = GetBoolConfigValue (config, "requiresQuestionAndAnswer", true);
595                         requiresUniqueEmail = GetBoolConfigValue (config, "requiresUniqueEmail", false);
596                         passwordFormat = (MembershipPasswordFormat) GetEnumConfigValue (config, "passwordFormat", typeof (MembershipPasswordFormat),
597                                                                                            (int) MembershipPasswordFormat.Hashed);
598                         maxInvalidPasswordAttempts = GetIntConfigValue (config, "maxInvalidPasswordAttempts", 5);
599                         minRequiredPasswordLength = GetIntConfigValue (config, "minRequiredPasswordLength", 7);
600                         minRequiredNonAlphanumericCharacters = GetIntConfigValue (config, "minRequiredNonAlphanumericCharacters", 1);
601                         passwordAttemptWindow = GetIntConfigValue (config, "passwordAttemptWindow", 10);
602                         passwordStrengthRegularExpression = GetStringConfigValue (config, "passwordStrengthRegularExpression", "");
603
604                         MembershipSection section = (MembershipSection) WebConfigurationManager.GetSection ("system.web/membership");
605
606                         userIsOnlineTimeWindow = section.UserIsOnlineTimeWindow;
607
608                         /* we can't support password retrieval with hashed passwords */
609                         if (passwordFormat == MembershipPasswordFormat.Hashed && enablePasswordRetrieval)
610                                 throw new ProviderException ("password retrieval cannot be used with hashed passwords");
611
612                         string connectionStringName = config ["connectionStringName"];
613
614                         if (applicationName.Length > 256)
615                                 throw new ProviderException ("The ApplicationName attribute must be 256 characters long or less.");
616                         if (connectionStringName == null || connectionStringName.Length == 0)
617                                 throw new ProviderException ("The ConnectionStringName attribute must be present and non-zero length.");
618
619                         connectionString = WebConfigurationManager.ConnectionStrings [connectionStringName];\r
620                         if (connectionString == null)\r
621                                 throw new ProviderException (String.Format ("The connection name '{0}' was not found in the applications configuration or the connection string is empty.", connectionStringName));\r
622 \r
623                         if (connectionString == null)\r
624                                 throw new ProviderException (String.Format ("The connection name '{0}' was not found in the applications configuration or the connection string is empty.", connectionStringName));\r
625 \r
626                         string shutdown = config ["shutdown"];\r
627                         if (!String.IsNullOrEmpty (shutdown))\r
628                                 shutDownPolicy = (DerbyUnloadManager.DerbyShutDownPolicy) Enum.Parse (typeof (DerbyUnloadManager.DerbyShutDownPolicy), shutdown, true);\r
629                 }
630
631                 public override string ResetPassword (string username, string answer)
632                 {
633                         if (!EnablePasswordReset)
634                                 throw new NotSupportedException ("this provider has not been configured to allow the resetting of passwords");
635
636                         CheckParam ("username", username, 256);
637
638                         if (RequiresQuestionAndAnswer)
639                                 CheckParam ("answer", answer, 128);
640
641                         using (DbConnection connection = CreateConnection ()) {
642                                 PasswordInfo pi = GetPasswordInfo (username);
643                                 if (pi == null)
644                                         throw new ProviderException (username + "is not found in the membership database");
645
646                                 string newPassword = GeneratePassword ();
647                                 EmitValidatingPassword (username, newPassword, false);
648
649                                 string db_password = EncodePassword (newPassword, pi.PasswordFormat, pi.PasswordSalt);
650                                 string db_answer = EncodePassword (answer, pi.PasswordFormat, pi.PasswordSalt);\r
651 \r
652                                 int st = DerbyMembershipHelper.Membership_ResetPassword (connection, ApplicationName, username, db_password, db_answer, (int) pi.PasswordFormat, pi.PasswordSalt, MaxInvalidPasswordAttempts, PasswordAttemptWindow, DateTime.UtcNow);
653
654                                 if (st == 0)
655                                         return newPassword;
656                                 else if (st == 1)\r
657                                         throw new ProviderException (username + " is not found in the membership database");\r
658                                 else if (st == 2)\r
659                                         throw new MembershipPasswordException ("The user account is currently locked out");\r
660                                 else if (st == 3)
661                                         throw new MembershipPasswordException ("Password Answer is invalid");
662                                 else
663                                         throw new ProviderException ("Failed to reset password");
664                         }
665                 }
666
667                 public override void UpdateUser (MembershipUser user)
668                 {
669                         if (user == null)
670                                 throw new ArgumentNullException ("user");
671
672                         if (user.UserName == null)
673                                 throw new ArgumentNullException ("user.UserName");
674
675                         if (RequiresUniqueEmail && user.Email == null)
676                                 throw new ArgumentNullException ("user.Email");
677
678                         CheckParam ("user.UserName", user.UserName, 256);
679
680                         if (user.Email.Length > 256 || (RequiresUniqueEmail && user.Email.Length == 0))
681                                 throw new ArgumentException ("invalid format for user.Email");
682
683                         using (DbConnection connection = CreateConnection ()) {\r
684                                 int st = DerbyMembershipHelper.Membership_UpdateUser (connection, ApplicationName, user.UserName, user.Email, user.Comment, user.IsApproved, RequiresUniqueEmail, user.LastLoginDate, DateTime.UtcNow, DateTime.UtcNow);
685
686                                 if (st == 1)
687                                         throw new ProviderException ("The UserName property of user was not found in the database.");
688                                 if (st == 2)
689                                         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.");
690                                 if (st != 0)
691                                         throw new ProviderException ("Failed to update user");
692                         }
693                 }
694
695                 public override bool ValidateUser (string username, string password)
696                 {
697                         if (username.Length == 0)
698                                 return false;
699
700                         CheckParam ("username", username, 256);
701                         EmitValidatingPassword (username, password, false);
702
703                         PasswordInfo pi = ValidateUsingPassword (username, password);
704                         if (pi != null) {
705                                 pi.LastLoginDate = DateTime.UtcNow;
706                                 UpdateUserInfo (username, pi, true, true);
707                                 return true;
708                         }
709                         return false;
710                 }
711
712                 public override bool UnlockUser (string username)
713                 {
714                         CheckParam ("username", username, 256);
715
716                         using (DbConnection connection = CreateConnection ()) {
717                                 try {\r
718                                         int st = DerbyMembershipHelper.Membership_UnlockUser (connection, ApplicationName, username);
719
720                                         if (st == 0)
721                                                 return true;
722                                 }
723                                 catch (Exception e) {
724                                         throw new ProviderException ("Failed to unlock user", e);
725                                 }
726                         }
727                         return false;
728                 }
729
730                 void UpdateUserInfo (string username, PasswordInfo pi, bool isPasswordCorrect, bool updateLoginActivity)
731                 {
732                         CheckParam ("username", username, 256);
733
734                         using (DbConnection connection = CreateConnection ()) {
735                                 try {\r
736                                         int st = DerbyMembershipHelper.Membership_UpdateUserInfo (connection, ApplicationName, username, isPasswordCorrect, updateLoginActivity,\r
737                                                 MaxInvalidPasswordAttempts, PasswordAttemptWindow, DateTime.UtcNow, pi.LastLoginDate, pi.LastActivityDate);
738
739                                         if (st == 0)
740                                                 return;
741                                 }
742                                 catch (Exception e) {
743                                         throw new ProviderException ("Failed to update Membership table", e);
744                                 }
745
746                         }
747                 }
748
749                 PasswordInfo ValidateUsingPassword (string username, string password)
750                 {
751                         MembershipUser user = GetUser (username, true);
752                         if (user == null)
753                                 return null;
754
755                         if (!user.IsApproved || user.IsLockedOut)
756                                 return null;
757
758                         PasswordInfo pi = GetPasswordInfo (username);
759
760                         if (pi == null)
761                                 return null;
762
763                         /* do the actual validation */
764                         string user_password = EncodePassword (password, pi.PasswordFormat, pi.PasswordSalt);
765
766                         if (user_password != pi.Password) {
767                                 UpdateUserInfo (username, pi, false, false);
768                                 return null;
769                         }
770
771                         return pi;
772                 }\r
773 \r
774                 private PasswordInfo GetPasswordInfo (string username)\r
775                 {\r
776                         using (DbConnection connection = CreateConnection ()) {\r
777                                 DbDataReader reader = null;\r
778                                 DerbyMembershipHelper.Membership_GetPasswordWithFormat (connection, ApplicationName, username, false, DateTime.UtcNow, out reader);\r
779 \r
780                                 PasswordInfo pi = null;\r
781                                 if (reader == null)\r
782                                         return null;\r
783 \r
784                                 using (reader) {\r
785                                         if (reader.Read ()) {\r
786                                                 int isLockedOut = reader.GetInt32 (1);\r
787                                                 if (isLockedOut > 0)\r
788                                                         return null;\r
789 \r
790                                                 pi = new PasswordInfo (\r
791                                                         reader.GetString (3),\r
792                                                         (MembershipPasswordFormat) reader.GetInt32 (4),\r
793                                                         reader.GetString (5),\r
794                                                         reader.GetInt32 (6),\r
795                                                         reader.GetInt32 (7),\r
796                                                         reader.GetInt32 (2) > 0,\r
797                                                         reader.GetDateTime (8),\r
798                                                         reader.GetDateTime (9));\r
799                                         }\r
800                                 }\r
801                                 return pi;\r
802                         }\r
803                 }\r
804 \r
805                 private string EncodePassword (string password, MembershipPasswordFormat passwordFormat, string salt)\r
806                 {\r
807                         byte [] password_bytes;\r
808                         byte [] salt_bytes;\r
809 \r
810                         switch (passwordFormat) {\r
811                                 case MembershipPasswordFormat.Clear:\r
812                                         return password;\r
813                                 case MembershipPasswordFormat.Hashed:\r
814                                         password_bytes = Encoding.Unicode.GetBytes (password);\r
815                                         salt_bytes = Convert.FromBase64String (salt);\r
816 \r
817                                         byte [] hashBytes = new byte [salt_bytes.Length + password_bytes.Length];\r
818 \r
819                                         Buffer.BlockCopy (salt_bytes, 0, hashBytes, 0, salt_bytes.Length);\r
820                                         Buffer.BlockCopy (password_bytes, 0, hashBytes, salt_bytes.Length, password_bytes.Length);\r
821 \r
822                                         MembershipSection section = (MembershipSection) WebConfigurationManager.GetSection ("system.web/membership");\r
823                                         string alg_type = section.HashAlgorithmType;\r
824                                         if (alg_type == "") {\r
825                                                 MachineKeySection keysection = (MachineKeySection) WebConfigurationManager.GetSection ("system.web/machineKey");\r
826                                                 alg_type = keysection.Validation.ToString ();\r
827                                         }\r
828                                         using (HashAlgorithm hash = HashAlgorithm.Create (alg_type)) {\r
829                                                 hash.TransformFinalBlock (hashBytes, 0, hashBytes.Length);\r
830                                                 return Convert.ToBase64String (hash.Hash);\r
831                                         }\r
832                                 case MembershipPasswordFormat.Encrypted:\r
833                                         password_bytes = Encoding.Unicode.GetBytes (password);\r
834                                         salt_bytes = Convert.FromBase64String (salt);\r
835 \r
836                                         byte [] buf = new byte [password_bytes.Length + salt_bytes.Length];\r
837 \r
838                                         Array.Copy (salt_bytes, 0, buf, 0, salt_bytes.Length);\r
839                                         Array.Copy (password_bytes, 0, buf, salt_bytes.Length, password_bytes.Length);\r
840 \r
841                                         return Convert.ToBase64String (EncryptPassword (buf));\r
842                                 default:\r
843                                         /* not reached.. */\r
844                                         return null;\r
845                         }\r
846                 }\r
847 \r
848                 private string DecodePassword (string password, MembershipPasswordFormat passwordFormat)\r
849                 {\r
850                         switch (passwordFormat) {\r
851                                 case MembershipPasswordFormat.Clear:\r
852                                         return password;\r
853                                 case MembershipPasswordFormat.Hashed:\r
854                                         throw new ProviderException ("Hashed passwords cannot be decoded.");\r
855                                 case MembershipPasswordFormat.Encrypted:\r
856                                         return Encoding.Unicode.GetString (DecryptPassword (Convert.FromBase64String (password)));\r
857                                 default:\r
858                                         /* not reached.. */\r
859                                         return null;\r
860                         }\r
861                 }
862
863                 public override string ApplicationName
864                 {
865                         get { return applicationName; }
866                         set { applicationName = value; }
867                 }
868
869                 public override bool EnablePasswordReset
870                 {
871                         get { return enablePasswordReset; }
872                 }
873
874                 public override bool EnablePasswordRetrieval
875                 {
876                         get { return enablePasswordRetrieval; }
877                 }
878
879                 public override MembershipPasswordFormat PasswordFormat
880                 {
881                         get { return passwordFormat; }
882                 }
883
884                 public override bool RequiresQuestionAndAnswer
885                 {
886                         get { return requiresQuestionAndAnswer; }
887                 }
888
889                 public override bool RequiresUniqueEmail
890                 {
891                         get { return requiresUniqueEmail; }
892                 }
893
894                 public override int MaxInvalidPasswordAttempts
895                 {
896                         get { return maxInvalidPasswordAttempts; }
897                 }
898
899                 public override int MinRequiredNonAlphanumericCharacters
900                 {
901                         get { return minRequiredNonAlphanumericCharacters; }
902                 }
903
904                 public override int MinRequiredPasswordLength
905                 {
906                         get { return minRequiredPasswordLength; }
907                 }
908
909                 public override int PasswordAttemptWindow
910                 {
911                         get { return passwordAttemptWindow; }
912                 }
913
914                 public override string PasswordStrengthRegularExpression
915                 {
916                         get { return passwordStrengthRegularExpression; }
917                 }
918
919                 [Flags]
920                 private enum DeleteUserTableMask
921                 {
922                         MembershipUsers = 1,
923                         UsersInRoles = 2,
924                         Profiles = 4,
925                         WebPartStateUser = 8
926                 }
927
928                 private sealed class PasswordInfo
929                 {
930                         private string _password;
931                         private MembershipPasswordFormat _passwordFormat;
932                         private string _passwordSalt;
933                         private int _failedPasswordAttemptCount;
934                         private int _failedPasswordAnswerAttemptCount;
935                         private bool _isApproved;
936                         private DateTime _lastLoginDate;
937                         private DateTime _lastActivityDate;
938
939                         internal PasswordInfo (
940                                 string password,
941                                 MembershipPasswordFormat passwordFormat,
942                                 string passwordSalt,
943                                 int failedPasswordAttemptCount,
944                                 int failedPasswordAnswerAttemptCount,
945                                 bool isApproved,
946                                 DateTime lastLoginDate,
947                                 DateTime lastActivityDate)
948                         {
949                                 _password = password;
950                                 _passwordFormat = passwordFormat;
951                                 _passwordSalt = passwordSalt;
952                                 _failedPasswordAttemptCount = failedPasswordAttemptCount;
953                                 _failedPasswordAnswerAttemptCount = failedPasswordAnswerAttemptCount;
954                                 _isApproved = isApproved;
955                                 _lastLoginDate = lastLoginDate;
956                                 _lastActivityDate = lastActivityDate;
957                         }
958
959                         public string Password
960                         {
961                                 get { return _password; }
962                                 set { _password = value; }
963                         }
964                         public MembershipPasswordFormat PasswordFormat
965                         {
966                                 get { return _passwordFormat; }
967                                 set { _passwordFormat = value; }
968                         }
969                         public string PasswordSalt
970                         {
971                                 get { return _passwordSalt; }
972                                 set { _passwordSalt = value; }
973                         }
974                         public int FailedPasswordAttemptCount
975                         {
976                                 get { return _failedPasswordAttemptCount; }
977                                 set { _failedPasswordAttemptCount = value; }
978                         }
979                         public int FailedPasswordAnswerAttemptCount
980                         {
981                                 get { return _failedPasswordAnswerAttemptCount; }
982                                 set { _failedPasswordAnswerAttemptCount = value; }
983                         }
984                         public bool IsApproved
985                         {
986                                 get { return _isApproved; }
987                                 set { _isApproved = value; }
988                         }
989                         public DateTime LastLoginDate
990                         {
991                                 get { return _lastLoginDate; }
992                                 set { _lastLoginDate = value; }
993                         }
994                         public DateTime LastActivityDate
995                         {
996                                 get { return _lastActivityDate; }
997                                 set { _lastActivityDate = value; }
998                         }
999                 }
1000         }
1001 }\r
1002