2 // Mainsoft.Web.Security.DerbyMembershipProvider
\r
5 // Ben Maurer (bmaurer@users.sourceforge.net)
\r
6 // Chris Toshok (toshok@ximian.com)
\r
7 // Vladimir Krasnov (vladimirk@mainsoft.com)
\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
18 // The above copyright notice and this permission notice shall be
\r
19 // included in all copies or substantial portions of the Software.
\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
33 using System.Collections;
34 using System.Collections.Specialized;
35 using System.Configuration;
36 using System.Configuration.Provider;
38 using System.Data.OleDb;
39 using System.Data.Common;
41 using System.Web.Configuration;
\r
42 using System.Security;
43 using System.Security.Cryptography;
\r
45 using System.Web.Security;
\r
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 DerbyDBSchema.CheckSchema (connectionString.ConnectionString);
\r
71 OleDbConnection connection = new OleDbConnection (connectionString.ConnectionString);
76 void CheckParam (string pName, string p, int length)
79 throw new ArgumentNullException (pName);
80 if (p.Length == 0 || p.Length > length || p.IndexOf (",") != -1)
81 throw new ArgumentException (String.Format ("invalid format for {0}", pName));
84 public override bool ChangePassword (string username, string oldPwd, string newPwd)
\r
86 if (username != null) username = username.Trim ();
\r
87 if (oldPwd != null) oldPwd = oldPwd.Trim ();
\r
88 if (newPwd != null) newPwd = newPwd.Trim ();
\r
90 CheckParam ("username", username, 256);
\r
91 CheckParam ("oldPwd", oldPwd, 128);
\r
92 CheckParam ("newPwd", newPwd, 128);
\r
94 using (DbConnection connection = CreateConnection ()) {
\r
95 PasswordInfo pi = ValidateUsingPassword (username, oldPwd);
\r
98 EmitValidatingPassword (username, newPwd, false);
\r
99 string db_password = EncodePassword (newPwd, pi.PasswordFormat, pi.PasswordSalt);
\r
101 int st = DerbyMembershipHelper.Membership_SetPassword (connection, ApplicationName, username, db_password, (int) pi.PasswordFormat, pi.PasswordSalt, DateTime.UtcNow);
\r
110 public override bool ChangePasswordQuestionAndAnswer (string username, string password, string newPwdQuestion, string newPwdAnswer)
112 if (username != null) username = username.Trim ();
113 if (newPwdQuestion != null) newPwdQuestion = newPwdQuestion.Trim ();
114 if (newPwdAnswer != null) newPwdAnswer = newPwdAnswer.Trim ();
116 CheckParam ("username", username, 256);
117 if (RequiresQuestionAndAnswer)
118 CheckParam ("newPwdQuestion", newPwdQuestion, 128);
119 if (RequiresQuestionAndAnswer)
120 CheckParam ("newPwdAnswer", newPwdAnswer, 128);
122 using (DbConnection connection = CreateConnection ()) {
123 PasswordInfo pi = ValidateUsingPassword (username, password);
126 string db_passwordAnswer = EncodePassword (newPwdAnswer, pi.PasswordFormat, pi.PasswordSalt);
\r
128 int st = DerbyMembershipHelper.Membership_ChangePasswordQuestionAndAnswer (connection, ApplicationName, username, newPwdQuestion, db_passwordAnswer);
137 public override MembershipUser CreateUser (string username,
143 object providerUserKey,
144 out MembershipCreateStatus status)
146 if (username != null) username = username.Trim ();
147 if (password != null) password = password.Trim ();
148 if (email != null) email = email.Trim ();
149 if (pwdQuestion != null) pwdQuestion = pwdQuestion.Trim ();
150 if (pwdAnswer != null) pwdAnswer = pwdAnswer.Trim ();
152 /* some initial validation */
153 if (username == null || username.Length == 0 || username.Length > 256 || username.IndexOf (",") != -1) {
154 status = MembershipCreateStatus.InvalidUserName;
157 if (password == null || password.Length == 0 || password.Length > 128) {
158 status = MembershipCreateStatus.InvalidPassword;
162 if (!CheckPassword (password)) {
163 status = MembershipCreateStatus.InvalidPassword;
166 EmitValidatingPassword (username, password, true);
168 if (RequiresUniqueEmail && (email == null || email.Length == 0)) {
169 status = MembershipCreateStatus.InvalidEmail;
172 if (RequiresQuestionAndAnswer &&
173 (pwdQuestion == null ||
174 pwdQuestion.Length == 0 || pwdQuestion.Length > 256)) {
175 status = MembershipCreateStatus.InvalidQuestion;
178 if (RequiresQuestionAndAnswer &&
179 (pwdAnswer == null ||
180 pwdAnswer.Length == 0 || pwdAnswer.Length > 128)) {
181 status = MembershipCreateStatus.InvalidAnswer;
184 if (providerUserKey != null && !(providerUserKey is Guid)) {
185 status = MembershipCreateStatus.InvalidProviderUserKey;
189 /* encode our password/answer using the
190 * "passwordFormat" configuration option */
191 string passwordSalt = "";
193 RandomNumberGenerator rng = RandomNumberGenerator.Create ();
194 byte [] salt = new byte [SALT_BYTES];
196 passwordSalt = Convert.ToBase64String (salt);
198 password = EncodePassword (password, PasswordFormat, passwordSalt);
199 if (RequiresQuestionAndAnswer)
200 pwdAnswer = EncodePassword (pwdAnswer, PasswordFormat, passwordSalt);
202 /* make sure the hashed/encrypted password and
203 * answer are still under 128 characters. */
204 if (password.Length > 128) {
205 status = MembershipCreateStatus.InvalidPassword;
209 if (RequiresQuestionAndAnswer) {
210 if (pwdAnswer.Length > 128) {
211 status = MembershipCreateStatus.InvalidAnswer;
215 status = MembershipCreateStatus.Success;
217 using (DbConnection connection = CreateConnection ()) {
220 object helperUserKey = providerUserKey != null ? providerUserKey.ToString () : null;
\r
221 DateTime Now = DateTime.UtcNow;
\r
222 int st = DerbyMembershipHelper.Membership_CreateUser (connection, ApplicationName, username, password, passwordSalt, email,
\r
223 pwdQuestion, pwdAnswer, isApproved, Now, Now, RequiresUniqueEmail, (int) PasswordFormat, ref helperUserKey);
\r
225 providerUserKey = new Guid ((string) helperUserKey);
\r
227 return GetUser (providerUserKey, false);
229 status = MembershipCreateStatus.DuplicateUserName;
231 status = MembershipCreateStatus.DuplicateEmail;
233 status = MembershipCreateStatus.InvalidProviderUserKey;
235 status = MembershipCreateStatus.DuplicateProviderUserKey;
237 status = MembershipCreateStatus.ProviderError;
\r
241 catch (Exception) {
\r
242 status = MembershipCreateStatus.ProviderError;
248 private bool CheckPassword (string password)
250 if (password.Length < MinRequiredPasswordLength)
253 if (MinRequiredNonAlphanumericCharacters > 0) {
254 int nonAlphanumeric = 0;
255 for (int i = 0; i < password.Length; i++) {
256 if (!Char.IsLetterOrDigit (password [i]))
259 return nonAlphanumeric >= MinRequiredNonAlphanumericCharacters;
264 public override bool DeleteUser (string username, bool deleteAllRelatedData)
266 CheckParam ("username", username, 256);
268 DeleteUserTableMask deleteBitmask = DeleteUserTableMask.MembershipUsers;
270 if (deleteAllRelatedData)
272 DeleteUserTableMask.Profiles |
273 DeleteUserTableMask.UsersInRoles |
274 DeleteUserTableMask.WebPartStateUser;
\r
277 using (DbConnection connection = CreateConnection ()) {
\r
278 int st = DerbyMembershipHelper.Users_DeleteUser (connection, ApplicationName, username, (int) deleteBitmask, ref num);
\r
290 public virtual string GeneratePassword ()
292 return Membership.GeneratePassword (MinRequiredPasswordLength, MinRequiredNonAlphanumericCharacters);
295 public override MembershipUserCollection FindUsersByEmail (string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
297 CheckParam ("emailToMatch", emailToMatch, 256);
300 throw new ArgumentException ("pageIndex must be >= 0");
302 throw new ArgumentException ("pageSize must be >= 0");
303 if (pageIndex * pageSize + pageSize - 1 > Int32.MaxValue)
304 throw new ArgumentException ("pageIndex and pageSize are too large");
\r
307 using (DbConnection connection = CreateConnection ()) {
\r
308 DbDataReader reader = null;
\r
310 DerbyMembershipHelper.Membership_FindUsersByEmail (connection, ApplicationName, emailToMatch, pageSize, pageIndex, out reader);
\r
311 if (reader == null)
\r
315 return BuildMembershipUserCollection (reader, pageIndex, pageSize, out totalRecords);
320 public override MembershipUserCollection FindUsersByName (string nameToMatch, int pageIndex, int pageSize, out int totalRecords)
322 CheckParam ("nameToMatch", nameToMatch, 256);
325 throw new ArgumentException ("pageIndex must be >= 0");
327 throw new ArgumentException ("pageSize must be >= 0");
328 if (pageIndex * pageSize + pageSize - 1 > Int32.MaxValue)
329 throw new ArgumentException ("pageIndex and pageSize are too large");
\r
332 using (DbConnection connection = CreateConnection ()) {
\r
333 DbDataReader reader = null;
\r
335 DerbyMembershipHelper.Membership_FindUsersByName (connection, ApplicationName, nameToMatch, pageSize, pageIndex, out reader);
\r
336 if (reader == null)
\r
340 return BuildMembershipUserCollection (reader, pageIndex, pageSize, out totalRecords);
\r
345 public override MembershipUserCollection GetAllUsers (int pageIndex, int pageSize, out int totalRecords)
348 throw new ArgumentException ("pageIndex must be >= 0");
350 throw new ArgumentException ("pageSize must be >= 0");
351 if (pageIndex * pageSize + pageSize - 1 > Int32.MaxValue)
352 throw new ArgumentException ("pageIndex and pageSize are too large");
354 using (DbConnection connection = CreateConnection ()) {
\r
355 DbDataReader reader = null;
\r
356 totalRecords = DerbyMembershipHelper.Membership_GetAllUsers (connection, ApplicationName, pageIndex, pageSize, out reader);
\r
357 return BuildMembershipUserCollection (reader, pageIndex, pageSize, out totalRecords);
\r
361 MembershipUserCollection BuildMembershipUserCollection (DbDataReader reader, int pageIndex, int pageSize, out int totalRecords)
365 int num_to_skip = pageIndex * pageSize;
\r
366 MembershipUserCollection users = new MembershipUserCollection ();
\r
368 while (reader.Read ()) {
\r
369 if (num_read >= num_to_skip) {
\r
370 if (num_added < pageSize) {
\r
371 users.Add (GetUserFromReader (reader));
\r
377 totalRecords = num_read;
\r
380 catch (Exception) {
\r
382 return null; /* should we let the exception through? */
\r
390 public override int GetNumberOfUsersOnline ()
392 using (DbConnection connection = CreateConnection ()) {
393 return DerbyMembershipHelper.Membership_GetNumberOfUsersOnline (connection, ApplicationName, userIsOnlineTimeWindow.Minutes, DateTime.UtcNow);
\r
397 public override string GetPassword (string username, string answer)
399 if (!EnablePasswordRetrieval)
400 throw new NotSupportedException ("this provider has not been configured to allow the retrieval of passwords");
402 CheckParam ("username", username, 256);
403 if (RequiresQuestionAndAnswer)
404 CheckParam ("answer", answer, 128);
406 PasswordInfo pi = GetPasswordInfo (username);
408 throw new ProviderException ("An error occurred while retrieving the password from the database");
410 string user_answer = EncodePassword (answer, pi.PasswordFormat, pi.PasswordSalt);
411 string password = null;
413 using (DbConnection connection = CreateConnection ()) {
\r
414 int st = DerbyMembershipHelper.Membership_GetPassword (connection, ApplicationName, username, user_answer, MaxInvalidPasswordAttempts, PasswordAttemptWindow, DateTime.UtcNow, out password);
\r
417 throw new ProviderException ("User specified by username is not found in the membership database");
\r
420 throw new MembershipPasswordException ("The membership user identified by username is locked out");
\r
423 throw new MembershipPasswordException ("Password Answer is invalid");
425 return DecodePassword (password, pi.PasswordFormat);
429 MembershipUser GetUserFromReader (DbDataReader reader)
\r
431 return new MembershipUser (
\r
432 this.Name, /* XXX is this right? */
\r
433 reader.GetString (0), /* name */
\r
434 new Guid (reader.GetString (1)), /* providerUserKey */
\r
435 reader.IsDBNull (2) ? null : reader.GetString (2), /* email */
\r
436 reader.IsDBNull (3) ? null : reader.GetString (3), /* passwordQuestion */
\r
437 reader.IsDBNull (4) ? null : reader.GetString (4), /* comment */
\r
438 reader.GetInt32 (5) > 0, /* isApproved */
\r
439 reader.GetInt32 (6) > 0, /* isLockedOut */
\r
440 reader.GetDateTime (7).ToLocalTime (), /* creationDate */
\r
441 reader.GetDateTime (8).ToLocalTime (), /* lastLoginDate */
\r
442 reader.GetDateTime (9).ToLocalTime (), /* lastActivityDate */
\r
443 reader.GetDateTime (10).ToLocalTime (), /* lastPasswordChangedDate */
\r
444 reader.GetDateTime (11).ToLocalTime () /* lastLockoutDate */);
\r
447 public override MembershipUser GetUser (string username, bool userIsOnline)
449 if (username.Length == 0)
452 CheckParam ("username", username, 256);
\r
454 using (DbConnection connection = CreateConnection ()) {
\r
455 DbDataReader reader = null;
\r
456 int st = DerbyMembershipHelper.Membership_GetUserByName (connection, ApplicationName, username, userIsOnline, DateTime.UtcNow, out reader);
\r
458 if (st == 0 && reader != null) {
\r
459 MembershipUser u = GetUserFromReader (reader);
\r
467 public override MembershipUser GetUser (object providerUserKey, bool userIsOnline)
469 if (providerUserKey == null)
\r
470 throw new ArgumentNullException ("providerUserKey");
\r
472 if (!(providerUserKey is Guid))
\r
473 throw new ArgumentException ("providerUserKey is not of type Guid", "providerUserKey");
\r
475 using (DbConnection connection = CreateConnection ()) {
\r
476 DbDataReader reader = null;
\r
477 int st = DerbyMembershipHelper.Membership_GetUserByUserId (connection, providerUserKey.ToString (), userIsOnline, DateTime.UtcNow, out reader);
\r
479 if (st == 0 && reader != null) {
\r
480 MembershipUser u = GetUserFromReader (reader);
\r
488 public override string GetUserNameByEmail (string email)
490 CheckParam ("email", email, 256);
\r
492 string username = null;
494 using (DbConnection connection = CreateConnection ()) {
\r
495 int st = DerbyMembershipHelper.Membership_GetUserByEmail (connection, ApplicationName, email, out username);
\r
500 if (st == 2 && RequiresUniqueEmail)
\r
501 throw new ProviderException ("More than one user with the same e-mail address exists in the database and RequiresUniqueEmail is true");
\r
506 bool GetBoolConfigValue (NameValueCollection config, string name, bool def)
509 string val = config [name];
511 try { rv = Boolean.Parse (val); }
512 catch (Exception e) {
513 throw new ProviderException (String.Format ("{0} must be true or false", name), e);
519 int GetIntConfigValue (NameValueCollection config, string name, int def)
522 string val = config [name];
524 try { rv = Int32.Parse (val); }
525 catch (Exception e) {
526 throw new ProviderException (String.Format ("{0} must be an integer", name), e);
532 int GetEnumConfigValue (NameValueCollection config, string name, Type enumType, int def)
535 string val = config [name];
537 try { rv = (int) Enum.Parse (enumType, val); }
538 catch (Exception e) {
539 throw new ProviderException (String.Format ("{0} must be one of the following values: {1}", name, String.Join (",", Enum.GetNames (enumType))), e);
545 string GetStringConfigValue (NameValueCollection config, string name, string def)
548 string val = config [name];
554 void EmitValidatingPassword (string username, string password, bool isNewUser)
556 ValidatePasswordEventArgs args = new ValidatePasswordEventArgs (username, password, isNewUser);
557 OnValidatingPassword (args);
559 /* if we're canceled.. */
561 if (args.FailureInformation == null)
562 throw new ProviderException ("Password validation canceled");
564 throw args.FailureInformation;
568 public override void Initialize (string name, NameValueCollection config)
571 throw new ArgumentNullException ("config");
573 base.Initialize (name, config);
575 applicationName = GetStringConfigValue (config, "applicationName", "/");
576 enablePasswordReset = GetBoolConfigValue (config, "enablePasswordReset", true);
577 enablePasswordRetrieval = GetBoolConfigValue (config, "enablePasswordRetrieval", false);
578 requiresQuestionAndAnswer = GetBoolConfigValue (config, "requiresQuestionAndAnswer", true);
579 requiresUniqueEmail = GetBoolConfigValue (config, "requiresUniqueEmail", false);
580 passwordFormat = (MembershipPasswordFormat) GetEnumConfigValue (config, "passwordFormat", typeof (MembershipPasswordFormat),
581 (int) MembershipPasswordFormat.Hashed);
582 maxInvalidPasswordAttempts = GetIntConfigValue (config, "maxInvalidPasswordAttempts", 5);
583 minRequiredPasswordLength = GetIntConfigValue (config, "minRequiredPasswordLength", 7);
584 minRequiredNonAlphanumericCharacters = GetIntConfigValue (config, "minRequiredNonAlphanumericCharacters", 1);
585 passwordAttemptWindow = GetIntConfigValue (config, "passwordAttemptWindow", 10);
586 passwordStrengthRegularExpression = GetStringConfigValue (config, "passwordStrengthRegularExpression", "");
588 MembershipSection section = (MembershipSection) WebConfigurationManager.GetSection ("system.web/membership");
590 userIsOnlineTimeWindow = section.UserIsOnlineTimeWindow;
592 /* we can't support password retrieval with hashed passwords */
593 if (passwordFormat == MembershipPasswordFormat.Hashed && enablePasswordRetrieval)
594 throw new ProviderException ("password retrieval cannot be used with hashed passwords");
596 string connectionStringName = config ["connectionStringName"];
598 if (applicationName.Length > 256)
599 throw new ProviderException ("The ApplicationName attribute must be 256 characters long or less.");
600 if (connectionStringName == null || connectionStringName.Length == 0)
601 throw new ProviderException ("The ConnectionStringName attribute must be present and non-zero length.");
603 connectionString = WebConfigurationManager.ConnectionStrings [connectionStringName];
\r
604 if (connectionString == null)
\r
605 throw new ProviderException (String.Format ("The connection name '{0}' was not found in the applications configuration or the connection string is empty.", connectionStringName));
\r
607 DerbyDBSchema.RegisterUnloadHandler (connectionString.ConnectionString);
\r
610 public override string ResetPassword (string username, string answer)
612 if (!EnablePasswordReset)
613 throw new NotSupportedException ("this provider has not been configured to allow the resetting of passwords");
615 CheckParam ("username", username, 256);
617 if (RequiresQuestionAndAnswer)
618 CheckParam ("answer", answer, 128);
620 using (DbConnection connection = CreateConnection ()) {
621 PasswordInfo pi = GetPasswordInfo (username);
623 throw new ProviderException (username + "is not found in the membership database");
625 string newPassword = GeneratePassword ();
626 EmitValidatingPassword (username, newPassword, false);
628 string db_password = EncodePassword (newPassword, pi.PasswordFormat, pi.PasswordSalt);
629 string db_answer = EncodePassword (answer, pi.PasswordFormat, pi.PasswordSalt);
\r
631 int st = DerbyMembershipHelper.Membership_ResetPassword (connection, ApplicationName, username, db_password, db_answer, (int) pi.PasswordFormat, pi.PasswordSalt, MaxInvalidPasswordAttempts, PasswordAttemptWindow, DateTime.UtcNow);
636 throw new ProviderException (username + " is not found in the membership database");
\r
638 throw new MembershipPasswordException ("The user account is currently locked out");
\r
640 throw new MembershipPasswordException ("Password Answer is invalid");
642 throw new ProviderException ("Failed to reset password");
646 public override void UpdateUser (MembershipUser user)
649 throw new ArgumentNullException ("user");
651 if (user.UserName == null)
652 throw new ArgumentNullException ("user.UserName");
654 if (RequiresUniqueEmail && user.Email == null)
655 throw new ArgumentNullException ("user.Email");
657 CheckParam ("user.UserName", user.UserName, 256);
659 if (user.Email.Length > 256 || (RequiresUniqueEmail && user.Email.Length == 0))
660 throw new ArgumentException ("invalid format for user.Email");
662 using (DbConnection connection = CreateConnection ()) {
\r
663 int st = DerbyMembershipHelper.Membership_UpdateUser (connection, ApplicationName, user.UserName, user.Email, user.Comment, user.IsApproved, RequiresUniqueEmail, user.LastLoginDate, DateTime.UtcNow, DateTime.UtcNow);
666 throw new ProviderException ("The UserName property of user was not found in the database.");
668 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.");
670 throw new ProviderException ("Failed to update user");
674 public override bool ValidateUser (string username, string password)
676 if (username.Length == 0)
679 CheckParam ("username", username, 256);
680 EmitValidatingPassword (username, password, false);
682 PasswordInfo pi = ValidateUsingPassword (username, password);
684 pi.LastLoginDate = DateTime.UtcNow;
685 UpdateUserInfo (username, pi, true, true);
691 public override bool UnlockUser (string username)
693 CheckParam ("username", username, 256);
695 using (DbConnection connection = CreateConnection ()) {
697 int st = DerbyMembershipHelper.Membership_UnlockUser (connection, ApplicationName, username);
702 catch (Exception e) {
703 throw new ProviderException ("Failed to unlock user", e);
709 void UpdateUserInfo (string username, PasswordInfo pi, bool isPasswordCorrect, bool updateLoginActivity)
711 CheckParam ("username", username, 256);
713 using (DbConnection connection = CreateConnection ()) {
715 int st = DerbyMembershipHelper.Membership_UpdateUserInfo (connection, ApplicationName, username, isPasswordCorrect, updateLoginActivity,
\r
716 MaxInvalidPasswordAttempts, PasswordAttemptWindow, DateTime.UtcNow, pi.LastLoginDate, pi.LastActivityDate);
721 catch (Exception e) {
722 throw new ProviderException ("Failed to update Membership table", e);
728 PasswordInfo ValidateUsingPassword (string username, string password)
730 MembershipUser user = GetUser (username, true);
734 if (!user.IsApproved || user.IsLockedOut)
737 PasswordInfo pi = GetPasswordInfo (username);
742 /* do the actual validation */
743 string user_password = EncodePassword (password, pi.PasswordFormat, pi.PasswordSalt);
745 if (user_password != pi.Password) {
746 UpdateUserInfo (username, pi, false, false);
753 private PasswordInfo GetPasswordInfo (string username)
\r
755 using (DbConnection connection = CreateConnection ()) {
\r
756 DbDataReader reader = null;
\r
757 DerbyMembershipHelper.Membership_GetPasswordWithFormat (connection, ApplicationName, username, false, DateTime.UtcNow, out reader);
\r
759 PasswordInfo pi = null;
\r
760 if (reader == null)
\r
764 if (reader.Read ()) {
\r
765 int isLockedOut = reader.GetInt32 (1);
\r
766 if (isLockedOut > 0)
\r
769 pi = new PasswordInfo (
\r
770 reader.GetString (3),
\r
771 (MembershipPasswordFormat) reader.GetInt32 (4),
\r
772 reader.GetString (5),
\r
773 reader.GetInt32 (6),
\r
774 reader.GetInt32 (7),
\r
775 reader.GetInt32 (2) > 0,
\r
776 reader.GetDateTime (8),
\r
777 reader.GetDateTime (9));
\r
784 private string EncodePassword (string password, MembershipPasswordFormat passwordFormat, string salt)
\r
786 byte [] password_bytes;
\r
787 byte [] salt_bytes;
\r
789 switch (passwordFormat) {
\r
790 case MembershipPasswordFormat.Clear:
\r
792 case MembershipPasswordFormat.Hashed:
\r
793 password_bytes = Encoding.Unicode.GetBytes (password);
\r
794 salt_bytes = Convert.FromBase64String (salt);
\r
796 byte [] hashBytes = new byte [salt_bytes.Length + password_bytes.Length];
\r
798 Buffer.BlockCopy (salt_bytes, 0, hashBytes, 0, salt_bytes.Length);
\r
799 Buffer.BlockCopy (password_bytes, 0, hashBytes, salt_bytes.Length, password_bytes.Length);
\r
801 MembershipSection section = (MembershipSection) WebConfigurationManager.GetSection ("system.web/membership");
\r
802 string alg_type = section.HashAlgorithmType;
\r
803 if (alg_type == "") {
\r
804 MachineKeySection keysection = (MachineKeySection) WebConfigurationManager.GetSection ("system.web/machineKey");
\r
805 alg_type = keysection.Validation.ToString ();
\r
807 using (HashAlgorithm hash = HashAlgorithm.Create (alg_type)) {
\r
808 hash.TransformFinalBlock (hashBytes, 0, hashBytes.Length);
\r
809 return Convert.ToBase64String (hash.Hash);
\r
811 case MembershipPasswordFormat.Encrypted:
\r
812 password_bytes = Encoding.Unicode.GetBytes (password);
\r
813 salt_bytes = Convert.FromBase64String (salt);
\r
815 byte [] buf = new byte [password_bytes.Length + salt_bytes.Length];
\r
817 Array.Copy (salt_bytes, 0, buf, 0, salt_bytes.Length);
\r
818 Array.Copy (password_bytes, 0, buf, salt_bytes.Length, password_bytes.Length);
\r
820 return Convert.ToBase64String (EncryptPassword (buf));
\r
822 /* not reached.. */
\r
827 private string DecodePassword (string password, MembershipPasswordFormat passwordFormat)
\r
829 switch (passwordFormat) {
\r
830 case MembershipPasswordFormat.Clear:
\r
832 case MembershipPasswordFormat.Hashed:
\r
833 throw new ProviderException ("Hashed passwords cannot be decoded.");
\r
834 case MembershipPasswordFormat.Encrypted:
\r
835 return Encoding.Unicode.GetString (DecryptPassword (Convert.FromBase64String (password)));
\r
837 /* not reached.. */
\r
842 public override string ApplicationName
844 get { return applicationName; }
845 set { applicationName = value; }
848 public override bool EnablePasswordReset
850 get { return enablePasswordReset; }
853 public override bool EnablePasswordRetrieval
855 get { return enablePasswordRetrieval; }
858 public override MembershipPasswordFormat PasswordFormat
860 get { return passwordFormat; }
863 public override bool RequiresQuestionAndAnswer
865 get { return requiresQuestionAndAnswer; }
868 public override bool RequiresUniqueEmail
870 get { return requiresUniqueEmail; }
873 public override int MaxInvalidPasswordAttempts
875 get { return maxInvalidPasswordAttempts; }
878 public override int MinRequiredNonAlphanumericCharacters
880 get { return minRequiredNonAlphanumericCharacters; }
883 public override int MinRequiredPasswordLength
885 get { return minRequiredPasswordLength; }
888 public override int PasswordAttemptWindow
890 get { return passwordAttemptWindow; }
893 public override string PasswordStrengthRegularExpression
895 get { return passwordStrengthRegularExpression; }
899 private enum DeleteUserTableMask
907 private sealed class PasswordInfo
909 private string _password;
910 private MembershipPasswordFormat _passwordFormat;
911 private string _passwordSalt;
912 private int _failedPasswordAttemptCount;
913 private int _failedPasswordAnswerAttemptCount;
914 private bool _isApproved;
915 private DateTime _lastLoginDate;
916 private DateTime _lastActivityDate;
918 internal PasswordInfo (
920 MembershipPasswordFormat passwordFormat,
922 int failedPasswordAttemptCount,
923 int failedPasswordAnswerAttemptCount,
925 DateTime lastLoginDate,
926 DateTime lastActivityDate)
928 _password = password;
929 _passwordFormat = passwordFormat;
930 _passwordSalt = passwordSalt;
931 _failedPasswordAttemptCount = failedPasswordAttemptCount;
932 _failedPasswordAnswerAttemptCount = failedPasswordAnswerAttemptCount;
933 _isApproved = isApproved;
934 _lastLoginDate = lastLoginDate;
935 _lastActivityDate = lastActivityDate;
938 public string Password
940 get { return _password; }
941 set { _password = value; }
943 public MembershipPasswordFormat PasswordFormat
945 get { return _passwordFormat; }
946 set { _passwordFormat = value; }
948 public string PasswordSalt
950 get { return _passwordSalt; }
951 set { _passwordSalt = value; }
953 public int FailedPasswordAttemptCount
955 get { return _failedPasswordAttemptCount; }
956 set { _failedPasswordAttemptCount = value; }
958 public int FailedPasswordAnswerAttemptCount
960 get { return _failedPasswordAnswerAttemptCount; }
961 set { _failedPasswordAnswerAttemptCount = value; }
963 public bool IsApproved
965 get { return _isApproved; }
966 set { _isApproved = value; }
968 public DateTime LastLoginDate
970 get { return _lastLoginDate; }
971 set { _lastLoginDate = value; }
973 public DateTime LastActivityDate
975 get { return _lastActivityDate; }
976 set { _lastActivityDate = value; }