In System.Windows.Forms:
[mono-project.git] / mcs / class / Mainsoft.Web / Mainsoft.Web.Security / DerbyMembershipProvider.cs
blob5f19282184e85c66cca0d9b7f9bef07bed20d01e
1 //
2 // Mainsoft.Web.Security.DerbyMembershipProvider
3 //
4 // Authors:
5 // Ben Maurer (bmaurer@users.sourceforge.net)
6 // Chris Toshok (toshok@ximian.com)
7 // Vladimir Krasnov (vladimirk@mainsoft.com)
8 //
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 //
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 //
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 #if NET_2_0
32 using System;
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.OleDb;
39 using System.Data.Common;
40 using System.Text;
41 using System.Web.Configuration;
42 using System.Security;
43 using System.Security.Cryptography;
44 using System.Web;
45 using System.Web.Security;
47 namespace Mainsoft.Web.Security {
48 public class DerbyMembershipProvider : MembershipProvider
50 const int SALT_BYTES = 16;
52 bool enablePasswordReset;
53 bool enablePasswordRetrieval;
54 int maxInvalidPasswordAttempts;
55 MembershipPasswordFormat passwordFormat;
56 bool requiresQuestionAndAnswer;
57 bool requiresUniqueEmail;
58 int minRequiredNonAlphanumericCharacters;
59 int minRequiredPasswordLength;
60 int passwordAttemptWindow;
61 string passwordStrengthRegularExpression;
62 TimeSpan userIsOnlineTimeWindow;
63 ConnectionStringSettings connectionString;
65 string applicationName;
67 DbConnection CreateConnection ()
69 OleDbConnection connection = new OleDbConnection (connectionString.ConnectionString);
70 connection.Open ();
71 return connection;
74 void CheckParam (string pName, string p, int length)
76 if (p == null)
77 throw new ArgumentNullException (pName);
78 if (p.Length == 0 || p.Length > length || p.IndexOf (",") != -1)
79 throw new ArgumentException (String.Format ("invalid format for {0}", pName));
82 public override bool ChangePassword (string username, string oldPwd, string newPwd)
84 if (username != null) username = username.Trim ();
85 if (oldPwd != null) oldPwd = oldPwd.Trim ();
86 if (newPwd != null) newPwd = newPwd.Trim ();
88 CheckParam ("username", username, 256);
89 CheckParam ("oldPwd", oldPwd, 128);
90 CheckParam ("newPwd", newPwd, 128);
92 using (DbConnection connection = CreateConnection ()) {
93 PasswordInfo pi = ValidateUsingPassword (username, oldPwd);
95 if (pi != null) {
96 EmitValidatingPassword (username, newPwd, false);
97 string db_password = EncodePassword (newPwd, pi.PasswordFormat, pi.PasswordSalt);
99 int st = DerbyMembershipHelper.Membership_SetPassword (connection, ApplicationName, username, newPwd, (int) pi.PasswordFormat, pi.PasswordSalt, DateTime.UtcNow);
101 if (st == 0)
102 return true;
104 return false;
108 public override bool ChangePasswordQuestionAndAnswer (string username, string password, string newPwdQuestion, string newPwdAnswer)
110 if (username != null) username = username.Trim ();
111 if (newPwdQuestion != null) newPwdQuestion = newPwdQuestion.Trim ();
112 if (newPwdAnswer != null) newPwdAnswer = newPwdAnswer.Trim ();
114 CheckParam ("username", username, 256);
115 if (RequiresQuestionAndAnswer)
116 CheckParam ("newPwdQuestion", newPwdQuestion, 128);
117 if (RequiresQuestionAndAnswer)
118 CheckParam ("newPwdAnswer", newPwdAnswer, 128);
120 using (DbConnection connection = CreateConnection ()) {
121 PasswordInfo pi = ValidateUsingPassword (username, password);
123 if (pi != null) {
124 string db_passwordAnswer = EncodePassword (newPwdAnswer, pi.PasswordFormat, pi.PasswordSalt);
126 int st = DerbyMembershipHelper.Membership_ChangePasswordQuestionAndAnswer (connection, ApplicationName, username, newPwdQuestion, newPwdAnswer);
128 if (st == 0)
129 return true;
131 return false;
135 public override MembershipUser CreateUser (string username,
136 string password,
137 string email,
138 string pwdQuestion,
139 string pwdAnswer,
140 bool isApproved,
141 object providerUserKey,
142 out MembershipCreateStatus status)
144 if (username != null) username = username.Trim ();
145 if (password != null) password = password.Trim ();
146 if (email != null) email = email.Trim ();
147 if (pwdQuestion != null) pwdQuestion = pwdQuestion.Trim ();
148 if (pwdAnswer != null) pwdAnswer = pwdAnswer.Trim ();
150 /* some initial validation */
151 if (username == null || username.Length == 0 || username.Length > 256 || username.IndexOf (",") != -1) {
152 status = MembershipCreateStatus.InvalidUserName;
153 return null;
155 if (password == null || password.Length == 0 || password.Length > 128) {
156 status = MembershipCreateStatus.InvalidPassword;
157 return null;
160 if (!CheckPassword (password)) {
161 status = MembershipCreateStatus.InvalidPassword;
162 return null;
164 EmitValidatingPassword (username, password, true);
166 if (RequiresUniqueEmail && (email == null || email.Length == 0)) {
167 status = MembershipCreateStatus.InvalidEmail;
168 return null;
170 if (RequiresQuestionAndAnswer &&
171 (pwdQuestion == null ||
172 pwdQuestion.Length == 0 || pwdQuestion.Length > 256)) {
173 status = MembershipCreateStatus.InvalidQuestion;
174 return null;
176 if (RequiresQuestionAndAnswer &&
177 (pwdAnswer == null ||
178 pwdAnswer.Length == 0 || pwdAnswer.Length > 128)) {
179 status = MembershipCreateStatus.InvalidAnswer;
180 return null;
182 if (providerUserKey != null && !(providerUserKey is string)) {
183 status = MembershipCreateStatus.InvalidProviderUserKey;
184 return null;
187 /* encode our password/answer using the
188 * "passwordFormat" configuration option */
189 string passwordSalt = "";
191 RandomNumberGenerator rng = RandomNumberGenerator.Create ();
192 byte [] salt = new byte [SALT_BYTES];
193 rng.GetBytes (salt);
194 passwordSalt = Convert.ToBase64String (salt);
196 password = EncodePassword (password, PasswordFormat, passwordSalt);
197 if (RequiresQuestionAndAnswer)
198 pwdAnswer = EncodePassword (pwdAnswer, PasswordFormat, passwordSalt);
200 /* make sure the hashed/encrypted password and
201 * answer are still under 128 characters. */
202 if (password.Length > 128) {
203 status = MembershipCreateStatus.InvalidPassword;
204 return null;
207 if (RequiresQuestionAndAnswer) {
208 if (pwdAnswer.Length > 128) {
209 status = MembershipCreateStatus.InvalidAnswer;
210 return null;
213 status = MembershipCreateStatus.Success;
215 using (DbConnection connection = CreateConnection ()) {
216 try {
219 DateTime Now = DateTime.UtcNow;
220 int st = DerbyMembershipHelper.Membership_CreateUser (connection, ApplicationName, username, password, passwordSalt, email,
221 pwdQuestion, pwdAnswer, isApproved, Now, Now, RequiresUniqueEmail, (int)PasswordFormat, ref providerUserKey);
223 if (st == 0)
224 return GetUser (providerUserKey, false);
225 else if (st == 2)
226 status = MembershipCreateStatus.DuplicateUserName;
227 else if (st == 3)
228 status = MembershipCreateStatus.DuplicateEmail;
229 else if (st == 9)
230 status = MembershipCreateStatus.InvalidProviderUserKey;
231 else if (st == 10)
232 status = MembershipCreateStatus.DuplicateProviderUserKey;
233 else
234 status = MembershipCreateStatus.ProviderError;
236 return null;
238 catch (Exception) {
239 status = MembershipCreateStatus.ProviderError;
240 return null;
245 private bool CheckPassword (string password)
247 if (password.Length < MinRequiredPasswordLength)
248 return false;
250 if (MinRequiredNonAlphanumericCharacters > 0) {
251 int nonAlphanumeric = 0;
252 for (int i = 0; i < password.Length; i++) {
253 if (!Char.IsLetterOrDigit (password [i]))
254 nonAlphanumeric++;
256 return nonAlphanumeric >= MinRequiredNonAlphanumericCharacters;
258 return true;
261 public override bool DeleteUser (string username, bool deleteAllRelatedData)
263 CheckParam ("username", username, 256);
265 DeleteUserTableMask deleteBitmask = DeleteUserTableMask.MembershipUsers;
267 if (deleteAllRelatedData)
268 deleteBitmask |=
269 DeleteUserTableMask.Profiles |
270 DeleteUserTableMask.UsersInRoles |
271 DeleteUserTableMask.WebPartStateUser;
273 int num = 0;
274 using (DbConnection connection = CreateConnection ()) {
275 int st = DerbyMembershipHelper.Users_DeleteUser (connection, ApplicationName, username, (int) deleteBitmask, ref num);
277 if (num == 0)
278 return false;
280 if (st == 0)
281 return true;
283 return false;
287 public virtual string GeneratePassword ()
289 return Membership.GeneratePassword (MinRequiredPasswordLength, MinRequiredNonAlphanumericCharacters);
292 public override MembershipUserCollection FindUsersByEmail (string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
294 CheckParam ("emailToMatch", emailToMatch, 256);
296 if (pageIndex < 0)
297 throw new ArgumentException ("pageIndex must be >= 0");
298 if (pageSize < 0)
299 throw new ArgumentException ("pageSize must be >= 0");
300 if (pageIndex * pageSize + pageSize - 1 > Int32.MaxValue)
301 throw new ArgumentException ("pageIndex and pageSize are too large");
303 totalRecords = 0;
304 using (DbConnection connection = CreateConnection ()) {
305 DbDataReader reader = null;
307 DerbyMembershipHelper.Membership_FindUsersByEmail (connection, ApplicationName, emailToMatch, pageSize, pageIndex, out reader);
308 if (reader == null)
309 return null;
311 using (reader) {
312 return BuildMembershipUserCollection (reader, pageIndex, pageSize, out totalRecords);
317 public override MembershipUserCollection FindUsersByName (string nameToMatch, int pageIndex, int pageSize, out int totalRecords)
319 CheckParam ("nameToMatch", nameToMatch, 256);
321 if (pageIndex < 0)
322 throw new ArgumentException ("pageIndex must be >= 0");
323 if (pageSize < 0)
324 throw new ArgumentException ("pageSize must be >= 0");
325 if (pageIndex * pageSize + pageSize - 1 > Int32.MaxValue)
326 throw new ArgumentException ("pageIndex and pageSize are too large");
328 totalRecords = 0;
329 using (DbConnection connection = CreateConnection ()) {
330 DbDataReader reader = null;
332 DerbyMembershipHelper.Membership_FindUsersByName (connection, ApplicationName, nameToMatch, pageSize, pageIndex, out reader);
333 if (reader == null)
334 return null;
336 using (reader) {
337 return BuildMembershipUserCollection (reader, pageIndex, pageSize, out totalRecords);
342 public override MembershipUserCollection GetAllUsers (int pageIndex, int pageSize, out int totalRecords)
344 if (pageIndex < 0)
345 throw new ArgumentException ("pageIndex must be >= 0");
346 if (pageSize < 0)
347 throw new ArgumentException ("pageSize must be >= 0");
348 if (pageIndex * pageSize + pageSize - 1 > Int32.MaxValue)
349 throw new ArgumentException ("pageIndex and pageSize are too large");
351 using (DbConnection connection = CreateConnection ()) {
352 DbDataReader reader = null;
353 totalRecords = DerbyMembershipHelper.Membership_GetAllUsers (connection, ApplicationName, pageIndex, pageSize, out reader);
354 return BuildMembershipUserCollection (reader, pageIndex, pageSize, out totalRecords);
358 MembershipUserCollection BuildMembershipUserCollection (DbDataReader reader, int pageIndex, int pageSize, out int totalRecords)
360 int num_read = 0;
361 int num_added = 0;
362 int num_to_skip = pageIndex * pageSize;
363 MembershipUserCollection users = new MembershipUserCollection ();
364 try {
365 while (reader.Read ()) {
366 if (num_read >= num_to_skip) {
367 if (num_added < pageSize) {
368 users.Add (GetUserFromReader (reader));
369 num_added++;
372 num_read++;
374 totalRecords = num_read;
375 return users;
377 catch (Exception) {
378 totalRecords = 0;
379 return null; /* should we let the exception through? */
381 finally {
382 if (reader != null)
383 reader.Close ();
387 public override int GetNumberOfUsersOnline ()
389 using (DbConnection connection = CreateConnection ()) {
390 return DerbyMembershipHelper.Membership_GetNumberOfUsersOnline (connection, ApplicationName, userIsOnlineTimeWindow.Minutes, DateTime.UtcNow);
394 public override string GetPassword (string username, string answer)
396 if (!EnablePasswordRetrieval)
397 throw new NotSupportedException ("this provider has not been configured to allow the retrieval of passwords");
399 CheckParam ("username", username, 256);
400 if (RequiresQuestionAndAnswer)
401 CheckParam ("answer", answer, 128);
403 PasswordInfo pi = GetPasswordInfo (username);
404 if (pi == null)
405 throw new ProviderException ("An error occurred while retrieving the password from the database");
407 string user_answer = EncodePassword (answer, pi.PasswordFormat, pi.PasswordSalt);
408 string password = null;
410 using (DbConnection connection = CreateConnection ()) {
411 int st = DerbyMembershipHelper.Membership_GetPassword (connection, ApplicationName, username, answer, MaxInvalidPasswordAttempts, PasswordAttemptWindow, DateTime.UtcNow, out password);
413 if (st == 1)
414 throw new ProviderException ("User specified by username is not found in the membership database");
416 if (st == 2)
417 throw new MembershipPasswordException ("The membership user identified by username is locked out");
419 if (st == 3)
420 throw new MembershipPasswordException ("Password Answer is invalid");
422 return DecodePassword (password, pi.PasswordFormat);
426 MembershipUser GetUserFromReader (DbDataReader reader)
428 return new MembershipUser (
429 this.Name, /* XXX is this right? */
430 reader.GetString (0), /* name */
431 reader.GetString (1), /* providerUserKey */
432 reader.IsDBNull (2) ? null : reader.GetString (2), /* email */
433 reader.IsDBNull (3) ? null : reader.GetString (3), /* passwordQuestion */
434 reader.IsDBNull (4) ? null : reader.GetString (4), /* comment */
435 reader.GetInt32 (5) > 0, /* isApproved */
436 reader.GetInt32 (6) > 0, /* isLockedOut */
437 reader.GetDateTime (7).ToLocalTime (), /* creationDate */
438 reader.GetDateTime (8).ToLocalTime (), /* lastLoginDate */
439 reader.GetDateTime (9).ToLocalTime (), /* lastActivityDate */
440 reader.GetDateTime (10).ToLocalTime (), /* lastPasswordChangedDate */
441 reader.GetDateTime (11).ToLocalTime () /* lastLockoutDate */);
444 public override MembershipUser GetUser (string username, bool userIsOnline)
446 if (username.Length == 0)
447 return null;
449 CheckParam ("username", username, 256);
451 using (DbConnection connection = CreateConnection ()) {
452 DbDataReader reader = null;
453 int st = DerbyMembershipHelper.Membership_GetUserByName (connection, ApplicationName, username, userIsOnline, DateTime.UtcNow, out reader);
454 using (reader) {
455 if (st == 0 && reader != null) {
456 MembershipUser u = GetUserFromReader (reader);
457 return u;
461 return null;
464 public override MembershipUser GetUser (object providerUserKey, bool userIsOnline)
466 if (providerUserKey == null)
467 throw new ArgumentNullException ("providerUserKey");
469 if (!(providerUserKey is string))
470 throw new ArgumentException ("providerUserKey is not of type string");
472 using (DbConnection connection = CreateConnection ()) {
473 DbDataReader reader = null;
474 int st = DerbyMembershipHelper.Membership_GetUserByUserId (connection, providerUserKey, userIsOnline, DateTime.UtcNow, out reader);
475 using (reader) {
476 if (st == 0 && reader != null) {
477 MembershipUser u = GetUserFromReader (reader);
478 return u;
482 return null;
485 public override string GetUserNameByEmail (string email)
487 CheckParam ("email", email, 256);
489 string username = null;
491 using (DbConnection connection = CreateConnection ()) {
492 int st = DerbyMembershipHelper.Membership_GetUserByEmail (connection, ApplicationName, email, out username);
494 if (st == 1)
495 return null;
497 if (st == 2 && RequiresUniqueEmail)
498 throw new ProviderException ("More than one user with the same e-mail address exists in the database and RequiresUniqueEmail is true");
500 return username;
503 bool GetBoolConfigValue (NameValueCollection config, string name, bool def)
505 bool rv = def;
506 string val = config [name];
507 if (val != null) {
508 try { rv = Boolean.Parse (val); }
509 catch (Exception e) {
510 throw new ProviderException (String.Format ("{0} must be true or false", name), e);
513 return rv;
516 int GetIntConfigValue (NameValueCollection config, string name, int def)
518 int rv = def;
519 string val = config [name];
520 if (val != null) {
521 try { rv = Int32.Parse (val); }
522 catch (Exception e) {
523 throw new ProviderException (String.Format ("{0} must be an integer", name), e);
526 return rv;
529 int GetEnumConfigValue (NameValueCollection config, string name, Type enumType, int def)
531 int rv = def;
532 string val = config [name];
533 if (val != null) {
534 try { rv = (int) Enum.Parse (enumType, val); }
535 catch (Exception e) {
536 throw new ProviderException (String.Format ("{0} must be one of the following values: {1}", name, String.Join (",", Enum.GetNames (enumType))), e);
539 return rv;
542 string GetStringConfigValue (NameValueCollection config, string name, string def)
544 string rv = def;
545 string val = config [name];
546 if (val != null)
547 rv = val;
548 return rv;
551 void EmitValidatingPassword (string username, string password, bool isNewUser)
553 ValidatePasswordEventArgs args = new ValidatePasswordEventArgs (username, password, isNewUser);
554 OnValidatingPassword (args);
556 /* if we're canceled.. */
557 if (args.Cancel) {
558 if (args.FailureInformation == null)
559 throw new ProviderException ("Password validation canceled");
560 else
561 throw args.FailureInformation;
565 public override void Initialize (string name, NameValueCollection config)
567 if (config == null)
568 throw new ArgumentNullException ("config");
570 base.Initialize (name, config);
572 applicationName = GetStringConfigValue (config, "applicationName", "/");
573 enablePasswordReset = GetBoolConfigValue (config, "enablePasswordReset", true);
574 enablePasswordRetrieval = GetBoolConfigValue (config, "enablePasswordRetrieval", false);
575 requiresQuestionAndAnswer = GetBoolConfigValue (config, "requiresQuestionAndAnswer", true);
576 requiresUniqueEmail = GetBoolConfigValue (config, "requiresUniqueEmail", false);
577 passwordFormat = (MembershipPasswordFormat) GetEnumConfigValue (config, "passwordFormat", typeof (MembershipPasswordFormat),
578 (int) MembershipPasswordFormat.Hashed);
579 maxInvalidPasswordAttempts = GetIntConfigValue (config, "maxInvalidPasswordAttempts", 5);
580 minRequiredPasswordLength = GetIntConfigValue (config, "minRequiredPasswordLength", 7);
581 minRequiredNonAlphanumericCharacters = GetIntConfigValue (config, "minRequiredNonAlphanumericCharacters", 1);
582 passwordAttemptWindow = GetIntConfigValue (config, "passwordAttemptWindow", 10);
583 passwordStrengthRegularExpression = GetStringConfigValue (config, "passwordStrengthRegularExpression", "");
585 MembershipSection section = (MembershipSection) WebConfigurationManager.GetSection ("system.web/membership");
587 userIsOnlineTimeWindow = section.UserIsOnlineTimeWindow;
589 /* we can't support password retrieval with hashed passwords */
590 if (passwordFormat == MembershipPasswordFormat.Hashed && enablePasswordRetrieval)
591 throw new ProviderException ("password retrieval cannot be used with hashed passwords");
593 string connectionStringName = config ["connectionStringName"];
595 if (applicationName.Length > 256)
596 throw new ProviderException ("The ApplicationName attribute must be 256 characters long or less.");
597 if (connectionStringName == null || connectionStringName.Length == 0)
598 throw new ProviderException ("The ConnectionStringName attribute must be present and non-zero length.");
600 connectionString = WebConfigurationManager.ConnectionStrings [connectionStringName];
602 DerbyDBSchema.InitializeSchema (connectionString.ConnectionString);
605 public override string ResetPassword (string username, string answer)
607 if (!EnablePasswordReset)
608 throw new NotSupportedException ("this provider has not been configured to allow the resetting of passwords");
610 CheckParam ("username", username, 256);
612 if (RequiresQuestionAndAnswer)
613 CheckParam ("answer", answer, 128);
615 using (DbConnection connection = CreateConnection ()) {
616 PasswordInfo pi = GetPasswordInfo (username);
617 if (pi == null)
618 throw new ProviderException (username + "is not found in the membership database");
620 string newPassword = GeneratePassword ();
621 EmitValidatingPassword (username, newPassword, false);
623 string db_password = EncodePassword (newPassword, pi.PasswordFormat, pi.PasswordSalt);
624 string db_answer = EncodePassword (answer, pi.PasswordFormat, pi.PasswordSalt);
626 int st = DerbyMembershipHelper.Membership_ResetPassword (connection, ApplicationName, username, db_password, db_answer, (int) pi.PasswordFormat, pi.PasswordSalt, MaxInvalidPasswordAttempts, PasswordAttemptWindow, DateTime.UtcNow);
628 if (st == 0)
629 return newPassword;
630 else if (st == 1)
631 throw new ProviderException (username + " is not found in the membership database");
632 else if (st == 2)
633 throw new MembershipPasswordException ("The user account is currently locked out");
634 else if (st == 3)
635 throw new MembershipPasswordException ("Password Answer is invalid");
636 else
637 throw new ProviderException ("Failed to reset password");
641 public override void UpdateUser (MembershipUser user)
643 if (user == null)
644 throw new ArgumentNullException ("user");
646 if (user.UserName == null)
647 throw new ArgumentNullException ("user.UserName");
649 if (RequiresUniqueEmail && user.Email == null)
650 throw new ArgumentNullException ("user.Email");
652 CheckParam ("user.UserName", user.UserName, 256);
654 if (user.Email.Length > 256 || (RequiresUniqueEmail && user.Email.Length == 0))
655 throw new ArgumentException ("invalid format for user.Email");
657 using (DbConnection connection = CreateConnection ()) {
658 int st = DerbyMembershipHelper.Membership_UpdateUser (connection, ApplicationName, user.UserName, user.Email, user.Comment, user.IsApproved, RequiresUniqueEmail, user.LastLoginDate, DateTime.UtcNow, DateTime.UtcNow);
660 if (st == 1)
661 throw new ProviderException ("The UserName property of user was not found in the database.");
662 if (st == 2)
663 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.");
664 if (st != 0)
665 throw new ProviderException ("Failed to update user");
669 public override bool ValidateUser (string username, string password)
671 if (username.Length == 0)
672 return false;
674 CheckParam ("username", username, 256);
675 EmitValidatingPassword (username, password, false);
677 PasswordInfo pi = ValidateUsingPassword (username, password);
678 if (pi != null) {
679 pi.LastLoginDate = DateTime.UtcNow;
680 UpdateUserInfo (username, pi, true, true);
681 return true;
683 return false;
686 public override bool UnlockUser (string username)
688 CheckParam ("username", username, 256);
690 using (DbConnection connection = CreateConnection ()) {
691 try {
692 int st = DerbyMembershipHelper.Membership_UnlockUser (connection, ApplicationName, username);
694 if (st == 0)
695 return true;
697 catch (Exception e) {
698 throw new ProviderException ("Failed to unlock user", e);
701 return false;
704 void UpdateUserInfo (string username, PasswordInfo pi, bool isPasswordCorrect, bool updateLoginActivity)
706 CheckParam ("username", username, 256);
708 using (DbConnection connection = CreateConnection ()) {
709 try {
710 int st = DerbyMembershipHelper.Membership_UpdateUserInfo (connection, ApplicationName, username, isPasswordCorrect, updateLoginActivity,
711 MaxInvalidPasswordAttempts, PasswordAttemptWindow, DateTime.UtcNow, pi.LastLoginDate, pi.LastActivityDate);
713 if (st == 0)
714 return;
716 catch (Exception e) {
717 throw new ProviderException ("Failed to update Membership table", e);
723 PasswordInfo ValidateUsingPassword (string username, string password)
725 MembershipUser user = GetUser (username, true);
726 if (user == null)
727 return null;
729 if (!user.IsApproved || user.IsLockedOut)
730 return null;
732 PasswordInfo pi = GetPasswordInfo (username);
734 if (pi == null)
735 return null;
737 /* do the actual validation */
738 string user_password = EncodePassword (password, pi.PasswordFormat, pi.PasswordSalt);
740 if (user_password != pi.Password) {
741 UpdateUserInfo (username, pi, false, false);
742 return null;
745 return pi;
748 private PasswordInfo GetPasswordInfo (string username)
750 using (DbConnection connection = CreateConnection ()) {
751 DbDataReader reader = null;
752 DerbyMembershipHelper.Membership_GetPasswordWithFormat (connection, ApplicationName, username, false, DateTime.UtcNow, out reader);
754 PasswordInfo pi = null;
755 if (reader == null)
756 return null;
758 using (reader) {
759 if (reader.Read ()) {
760 int isLockedOut = reader.GetInt32 (1);
761 if (isLockedOut > 0)
762 return null;
764 pi = new PasswordInfo (
765 reader.GetString (3),
766 (MembershipPasswordFormat) reader.GetInt32 (4),
767 reader.GetString (5),
768 reader.GetInt32 (6),
769 reader.GetInt32 (7),
770 reader.GetInt32 (2) > 0,
771 reader.GetDateTime (8),
772 reader.GetDateTime (9));
775 return pi;
779 private string EncodePassword (string password, MembershipPasswordFormat passwordFormat, string salt)
781 byte [] password_bytes;
782 byte [] salt_bytes;
784 switch (passwordFormat) {
785 case MembershipPasswordFormat.Clear:
786 return password;
787 case MembershipPasswordFormat.Hashed:
788 password_bytes = Encoding.Unicode.GetBytes (password);
789 salt_bytes = Convert.FromBase64String (salt);
791 byte [] hashBytes = new byte [salt_bytes.Length + password_bytes.Length];
793 Buffer.BlockCopy (salt_bytes, 0, hashBytes, 0, salt_bytes.Length);
794 Buffer.BlockCopy (password_bytes, 0, hashBytes, salt_bytes.Length, password_bytes.Length);
796 MembershipSection section = (MembershipSection) WebConfigurationManager.GetSection ("system.web/membership");
797 string alg_type = section.HashAlgorithmType;
798 if (alg_type == "") {
799 MachineKeySection keysection = (MachineKeySection) WebConfigurationManager.GetSection ("system.web/machineKey");
800 alg_type = keysection.Validation.ToString ();
802 using (HashAlgorithm hash = HashAlgorithm.Create (alg_type)) {
803 hash.TransformFinalBlock (hashBytes, 0, hashBytes.Length);
804 return Convert.ToBase64String (hash.Hash);
806 case MembershipPasswordFormat.Encrypted:
807 password_bytes = Encoding.Unicode.GetBytes (password);
808 salt_bytes = Convert.FromBase64String (salt);
810 byte [] buf = new byte [password_bytes.Length + salt_bytes.Length];
812 Array.Copy (salt_bytes, 0, buf, 0, salt_bytes.Length);
813 Array.Copy (password_bytes, 0, buf, salt_bytes.Length, password_bytes.Length);
815 return Convert.ToBase64String (EncryptPassword (buf));
816 default:
817 /* not reached.. */
818 return null;
822 private string DecodePassword (string password, MembershipPasswordFormat passwordFormat)
824 switch (passwordFormat) {
825 case MembershipPasswordFormat.Clear:
826 return password;
827 case MembershipPasswordFormat.Hashed:
828 throw new ProviderException ("Hashed passwords cannot be decoded.");
829 case MembershipPasswordFormat.Encrypted:
830 return Encoding.Unicode.GetString (DecryptPassword (Convert.FromBase64String (password)));
831 default:
832 /* not reached.. */
833 return null;
837 public override string ApplicationName
839 get { return applicationName; }
840 set { applicationName = value; }
843 public override bool EnablePasswordReset
845 get { return enablePasswordReset; }
848 public override bool EnablePasswordRetrieval
850 get { return enablePasswordRetrieval; }
853 public override MembershipPasswordFormat PasswordFormat
855 get { return passwordFormat; }
858 public override bool RequiresQuestionAndAnswer
860 get { return requiresQuestionAndAnswer; }
863 public override bool RequiresUniqueEmail
865 get { return requiresUniqueEmail; }
868 public override int MaxInvalidPasswordAttempts
870 get { return maxInvalidPasswordAttempts; }
873 public override int MinRequiredNonAlphanumericCharacters
875 get { return minRequiredNonAlphanumericCharacters; }
878 public override int MinRequiredPasswordLength
880 get { return minRequiredPasswordLength; }
883 public override int PasswordAttemptWindow
885 get { return passwordAttemptWindow; }
888 public override string PasswordStrengthRegularExpression
890 get { return passwordStrengthRegularExpression; }
893 [Flags]
894 private enum DeleteUserTableMask
896 MembershipUsers = 1,
897 UsersInRoles = 2,
898 Profiles = 4,
899 WebPartStateUser = 8
902 private sealed class PasswordInfo
904 private string _password;
905 private MembershipPasswordFormat _passwordFormat;
906 private string _passwordSalt;
907 private int _failedPasswordAttemptCount;
908 private int _failedPasswordAnswerAttemptCount;
909 private bool _isApproved;
910 private DateTime _lastLoginDate;
911 private DateTime _lastActivityDate;
913 internal PasswordInfo (
914 string password,
915 MembershipPasswordFormat passwordFormat,
916 string passwordSalt,
917 int failedPasswordAttemptCount,
918 int failedPasswordAnswerAttemptCount,
919 bool isApproved,
920 DateTime lastLoginDate,
921 DateTime lastActivityDate)
923 _password = password;
924 _passwordFormat = passwordFormat;
925 _passwordSalt = passwordSalt;
926 _failedPasswordAttemptCount = failedPasswordAttemptCount;
927 _failedPasswordAnswerAttemptCount = failedPasswordAnswerAttemptCount;
928 _isApproved = isApproved;
929 _lastLoginDate = lastLoginDate;
930 _lastActivityDate = lastActivityDate;
933 public string Password
935 get { return _password; }
936 set { _password = value; }
938 public MembershipPasswordFormat PasswordFormat
940 get { return _passwordFormat; }
941 set { _passwordFormat = value; }
943 public string PasswordSalt
945 get { return _passwordSalt; }
946 set { _passwordSalt = value; }
948 public int FailedPasswordAttemptCount
950 get { return _failedPasswordAttemptCount; }
951 set { _failedPasswordAttemptCount = value; }
953 public int FailedPasswordAnswerAttemptCount
955 get { return _failedPasswordAnswerAttemptCount; }
956 set { _failedPasswordAnswerAttemptCount = value; }
958 public bool IsApproved
960 get { return _isApproved; }
961 set { _isApproved = value; }
963 public DateTime LastLoginDate
965 get { return _lastLoginDate; }
966 set { _lastLoginDate = value; }
968 public DateTime LastActivityDate
970 get { return _lastActivityDate; }
971 set { _lastActivityDate = value; }
976 #endif