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
32 using System.Collections;
33 using System.Collections.Specialized;
34 using System.Configuration;
35 using System.Configuration.Provider;
37 using System.Data.OleDb;
38 using System.Data.Common;
40 using System.Web.Configuration;
\r
41 using System.Security;
42 using System.Security.Cryptography;
\r
44 using System.Web.Security;
\r
46 namespace Mainsoft.Web.Security {
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
51 public class DerbyMembershipProvider : MembershipProvider
53 const int SALT_BYTES = 16;
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;
70 string applicationName;
72 DbConnection CreateConnection ()
74 if (!schemaChecked) {
\r
75 DerbyDBSchema.CheckSchema (connectionString.ConnectionString);
\r
76 schemaChecked = true;
\r
78 DerbyUnloadManager.RegisterUnloadHandler (connectionString.ConnectionString, shutDownPolicy);
\r
81 OleDbConnection connection = new OleDbConnection (connectionString.ConnectionString);
86 void CheckParam (string pName, string p, int length)
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));
94 public override bool ChangePassword (string username, string oldPwd, string newPwd)
\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
100 CheckParam ("username", username, 256);
\r
101 CheckParam ("oldPwd", oldPwd, 128);
\r
102 CheckParam ("newPwd", newPwd, 128);
\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
110 using (DbConnection connection = CreateConnection ()) {
\r
111 PasswordInfo pi = ValidateUsingPassword (username, oldPwd);
\r
114 EmitValidatingPassword (username, newPwd, false);
\r
115 string db_password = EncodePassword (newPwd, pi.PasswordFormat, pi.PasswordSalt);
\r
117 int st = DerbyMembershipHelper.Membership_SetPassword (connection, ApplicationName, username, db_password, (int) pi.PasswordFormat, pi.PasswordSalt, DateTime.UtcNow);
\r
126 public override bool ChangePasswordQuestionAndAnswer (string username, string password, string newPwdQuestion, string newPwdAnswer)
128 if (username != null) username = username.Trim ();
129 if (newPwdQuestion != null) newPwdQuestion = newPwdQuestion.Trim ();
130 if (newPwdAnswer != null) newPwdAnswer = newPwdAnswer.Trim ();
132 CheckParam ("username", username, 256);
133 if (RequiresQuestionAndAnswer)
134 CheckParam ("newPwdQuestion", newPwdQuestion, 128);
135 if (RequiresQuestionAndAnswer)
136 CheckParam ("newPwdAnswer", newPwdAnswer, 128);
138 using (DbConnection connection = CreateConnection ()) {
139 PasswordInfo pi = ValidateUsingPassword (username, password);
142 string db_passwordAnswer = EncodePassword (newPwdAnswer, pi.PasswordFormat, pi.PasswordSalt);
\r
144 int st = DerbyMembershipHelper.Membership_ChangePasswordQuestionAndAnswer (connection, ApplicationName, username, newPwdQuestion, db_passwordAnswer);
153 public override MembershipUser CreateUser (string username,
159 object providerUserKey,
160 out MembershipCreateStatus status)
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 ();
168 /* some initial validation */
169 if (username == null || username.Length == 0 || username.Length > 256 || username.IndexOf (",") != -1) {
170 status = MembershipCreateStatus.InvalidUserName;
173 if (password == null || password.Length == 0 || password.Length > 128) {
174 status = MembershipCreateStatus.InvalidPassword;
178 if (!CheckPassword (password)) {
179 status = MembershipCreateStatus.InvalidPassword;
182 EmitValidatingPassword (username, password, true);
184 if (RequiresUniqueEmail && (email == null || email.Length == 0)) {
185 status = MembershipCreateStatus.InvalidEmail;
188 if (RequiresQuestionAndAnswer &&
189 (pwdQuestion == null ||
190 pwdQuestion.Length == 0 || pwdQuestion.Length > 256)) {
191 status = MembershipCreateStatus.InvalidQuestion;
194 if (RequiresQuestionAndAnswer &&
195 (pwdAnswer == null ||
196 pwdAnswer.Length == 0 || pwdAnswer.Length > 128)) {
197 status = MembershipCreateStatus.InvalidAnswer;
200 if (providerUserKey != null && !(providerUserKey is Guid)) {
201 status = MembershipCreateStatus.InvalidProviderUserKey;
205 /* encode our password/answer using the
206 * "passwordFormat" configuration option */
207 string passwordSalt = "";
209 RandomNumberGenerator rng = RandomNumberGenerator.Create ();
210 byte [] salt = new byte [SALT_BYTES];
212 passwordSalt = Convert.ToBase64String (salt);
214 password = EncodePassword (password, PasswordFormat, passwordSalt);
215 if (RequiresQuestionAndAnswer)
216 pwdAnswer = EncodePassword (pwdAnswer, PasswordFormat, passwordSalt);
218 /* make sure the hashed/encrypted password and
219 * answer are still under 128 characters. */
220 if (password.Length > 128) {
221 status = MembershipCreateStatus.InvalidPassword;
225 if (RequiresQuestionAndAnswer) {
226 if (pwdAnswer.Length > 128) {
227 status = MembershipCreateStatus.InvalidAnswer;
231 status = MembershipCreateStatus.Success;
233 using (DbConnection connection = CreateConnection ()) {
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
241 providerUserKey = new Guid ((string) helperUserKey);
\r
243 return GetUser (providerUserKey, false);
245 status = MembershipCreateStatus.DuplicateUserName;
247 status = MembershipCreateStatus.DuplicateEmail;
249 status = MembershipCreateStatus.InvalidProviderUserKey;
251 status = MembershipCreateStatus.DuplicateProviderUserKey;
253 status = MembershipCreateStatus.ProviderError;
\r
257 catch (Exception) {
\r
258 status = MembershipCreateStatus.ProviderError;
264 private bool CheckPassword (string password)
266 if (password.Length < MinRequiredPasswordLength)
269 if (MinRequiredNonAlphanumericCharacters > 0) {
270 int nonAlphanumeric = 0;
271 for (int i = 0; i < password.Length; i++) {
272 if (!Char.IsLetterOrDigit (password [i]))
275 return nonAlphanumeric >= MinRequiredNonAlphanumericCharacters;
280 public override bool DeleteUser (string username, bool deleteAllRelatedData)
282 CheckParam ("username", username, 256);
284 DeleteUserTableMask deleteBitmask = DeleteUserTableMask.MembershipUsers;
286 if (deleteAllRelatedData)
288 DeleteUserTableMask.Profiles |
289 DeleteUserTableMask.UsersInRoles |
290 DeleteUserTableMask.WebPartStateUser;
\r
293 using (DbConnection connection = CreateConnection ()) {
\r
294 int st = DerbyMembershipHelper.Users_DeleteUser (connection, ApplicationName, username, (int) deleteBitmask, ref num);
\r
306 public virtual string GeneratePassword ()
308 return Membership.GeneratePassword (MinRequiredPasswordLength, MinRequiredNonAlphanumericCharacters);
311 public override MembershipUserCollection FindUsersByEmail (string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
313 CheckParam ("emailToMatch", emailToMatch, 256);
316 throw new ArgumentException ("pageIndex must be >= 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
323 using (DbConnection connection = CreateConnection ()) {
\r
324 DbDataReader reader = null;
\r
326 DerbyMembershipHelper.Membership_FindUsersByEmail (connection, ApplicationName, emailToMatch, pageSize, pageIndex, out reader);
\r
327 if (reader == null)
\r
331 return BuildMembershipUserCollection (reader, pageIndex, pageSize, out totalRecords);
336 public override MembershipUserCollection FindUsersByName (string nameToMatch, int pageIndex, int pageSize, out int totalRecords)
338 CheckParam ("nameToMatch", nameToMatch, 256);
341 throw new ArgumentException ("pageIndex must be >= 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
348 using (DbConnection connection = CreateConnection ()) {
\r
349 DbDataReader reader = null;
\r
351 DerbyMembershipHelper.Membership_FindUsersByName (connection, ApplicationName, nameToMatch, pageSize, pageIndex, out reader);
\r
352 if (reader == null)
\r
356 return BuildMembershipUserCollection (reader, pageIndex, pageSize, out totalRecords);
\r
361 public override MembershipUserCollection GetAllUsers (int pageIndex, int pageSize, out int totalRecords)
364 throw new ArgumentException ("pageIndex must be >= 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");
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
377 MembershipUserCollection BuildMembershipUserCollection (DbDataReader reader, int pageIndex, int pageSize, out int totalRecords)
381 int num_to_skip = pageIndex * pageSize;
\r
382 MembershipUserCollection users = new MembershipUserCollection ();
\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
393 totalRecords = num_read;
\r
396 catch (Exception) {
\r
398 return null; /* should we let the exception through? */
\r
406 public override int GetNumberOfUsersOnline ()
408 using (DbConnection connection = CreateConnection ()) {
409 return DerbyMembershipHelper.Membership_GetNumberOfUsersOnline (connection, ApplicationName, userIsOnlineTimeWindow.Minutes, DateTime.UtcNow);
\r
413 public override string GetPassword (string username, string answer)
415 if (!EnablePasswordRetrieval)
416 throw new NotSupportedException ("this provider has not been configured to allow the retrieval of passwords");
418 CheckParam ("username", username, 256);
419 if (RequiresQuestionAndAnswer)
420 CheckParam ("answer", answer, 128);
422 PasswordInfo pi = GetPasswordInfo (username);
424 throw new ProviderException ("An error occurred while retrieving the password from the database");
426 string user_answer = EncodePassword (answer, pi.PasswordFormat, pi.PasswordSalt);
427 string password = null;
429 using (DbConnection connection = CreateConnection ()) {
\r
430 int st = DerbyMembershipHelper.Membership_GetPassword (connection, ApplicationName, username, user_answer, MaxInvalidPasswordAttempts, PasswordAttemptWindow, DateTime.UtcNow, out password);
\r
433 throw new ProviderException ("User specified by username is not found in the membership database");
\r
436 throw new MembershipPasswordException ("The membership user identified by username is locked out");
\r
439 throw new MembershipPasswordException ("Password Answer is invalid");
441 return DecodePassword (password, pi.PasswordFormat);
445 MembershipUser GetUserFromReader (DbDataReader reader)
\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
463 public override MembershipUser GetUser (string username, bool userIsOnline)
465 if (username.Length == 0)
468 CheckParam ("username", username, 256);
\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
474 if (st == 0 && reader != null) {
\r
475 MembershipUser u = GetUserFromReader (reader);
\r
483 public override MembershipUser GetUser (object providerUserKey, bool userIsOnline)
485 if (providerUserKey == null)
\r
486 throw new ArgumentNullException ("providerUserKey");
\r
488 if (!(providerUserKey is Guid))
\r
489 throw new ArgumentException ("providerUserKey is not of type Guid", "providerUserKey");
\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
495 if (st == 0 && reader != null) {
\r
496 MembershipUser u = GetUserFromReader (reader);
\r
504 public override string GetUserNameByEmail (string email)
506 CheckParam ("email", email, 256);
\r
508 string username = null;
510 using (DbConnection connection = CreateConnection ()) {
\r
511 int st = DerbyMembershipHelper.Membership_GetUserByEmail (connection, ApplicationName, email, out username);
\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
522 bool GetBoolConfigValue (NameValueCollection config, string name, bool def)
525 string val = config [name];
527 try { rv = Boolean.Parse (val); }
528 catch (Exception e) {
529 throw new ProviderException (String.Format ("{0} must be true or false", name), e);
535 int GetIntConfigValue (NameValueCollection config, string name, int def)
538 string val = config [name];
540 try { rv = Int32.Parse (val); }
541 catch (Exception e) {
542 throw new ProviderException (String.Format ("{0} must be an integer", name), e);
548 int GetEnumConfigValue (NameValueCollection config, string name, Type enumType, int def)
551 string val = config [name];
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);
561 string GetStringConfigValue (NameValueCollection config, string name, string def)
564 string val = config [name];
570 void EmitValidatingPassword (string username, string password, bool isNewUser)
572 ValidatePasswordEventArgs args = new ValidatePasswordEventArgs (username, password, isNewUser);
573 OnValidatingPassword (args);
575 /* if we're canceled.. */
577 if (args.FailureInformation == null)
578 throw new ProviderException ("Password validation canceled");
580 throw args.FailureInformation;
584 public override void Initialize (string name, NameValueCollection config)
587 throw new ArgumentNullException ("config");
589 base.Initialize (name, config);
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", "");
604 MembershipSection section = (MembershipSection) WebConfigurationManager.GetSection ("system.web/membership");
606 userIsOnlineTimeWindow = section.UserIsOnlineTimeWindow;
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");
612 string connectionStringName = config ["connectionStringName"];
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.");
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
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
626 string shutdown = config ["shutdown"];
\r
627 if (!String.IsNullOrEmpty (shutdown))
\r
628 shutDownPolicy = (DerbyUnloadManager.DerbyShutDownPolicy) Enum.Parse (typeof (DerbyUnloadManager.DerbyShutDownPolicy), shutdown, true);
\r
631 public override string ResetPassword (string username, string answer)
633 if (!EnablePasswordReset)
634 throw new NotSupportedException ("this provider has not been configured to allow the resetting of passwords");
636 CheckParam ("username", username, 256);
638 if (RequiresQuestionAndAnswer)
639 CheckParam ("answer", answer, 128);
641 using (DbConnection connection = CreateConnection ()) {
642 PasswordInfo pi = GetPasswordInfo (username);
644 throw new ProviderException (username + "is not found in the membership database");
646 string newPassword = GeneratePassword ();
647 EmitValidatingPassword (username, newPassword, false);
649 string db_password = EncodePassword (newPassword, pi.PasswordFormat, pi.PasswordSalt);
650 string db_answer = EncodePassword (answer, pi.PasswordFormat, pi.PasswordSalt);
\r
652 int st = DerbyMembershipHelper.Membership_ResetPassword (connection, ApplicationName, username, db_password, db_answer, (int) pi.PasswordFormat, pi.PasswordSalt, MaxInvalidPasswordAttempts, PasswordAttemptWindow, DateTime.UtcNow);
657 throw new ProviderException (username + " is not found in the membership database");
\r
659 throw new MembershipPasswordException ("The user account is currently locked out");
\r
661 throw new MembershipPasswordException ("Password Answer is invalid");
663 throw new ProviderException ("Failed to reset password");
667 public override void UpdateUser (MembershipUser user)
670 throw new ArgumentNullException ("user");
672 if (user.UserName == null)
673 throw new ArgumentNullException ("user.UserName");
675 if (RequiresUniqueEmail && user.Email == null)
676 throw new ArgumentNullException ("user.Email");
678 CheckParam ("user.UserName", user.UserName, 256);
680 if (user.Email.Length > 256 || (RequiresUniqueEmail && user.Email.Length == 0))
681 throw new ArgumentException ("invalid format for user.Email");
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);
687 throw new ProviderException ("The UserName property of user was not found in the database.");
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.");
691 throw new ProviderException ("Failed to update user");
695 public override bool ValidateUser (string username, string password)
697 if (username.Length == 0)
700 CheckParam ("username", username, 256);
701 EmitValidatingPassword (username, password, false);
703 PasswordInfo pi = ValidateUsingPassword (username, password);
705 pi.LastLoginDate = DateTime.UtcNow;
706 UpdateUserInfo (username, pi, true, true);
712 public override bool UnlockUser (string username)
714 CheckParam ("username", username, 256);
716 using (DbConnection connection = CreateConnection ()) {
718 int st = DerbyMembershipHelper.Membership_UnlockUser (connection, ApplicationName, username);
723 catch (Exception e) {
724 throw new ProviderException ("Failed to unlock user", e);
730 void UpdateUserInfo (string username, PasswordInfo pi, bool isPasswordCorrect, bool updateLoginActivity)
732 CheckParam ("username", username, 256);
734 using (DbConnection connection = CreateConnection ()) {
736 int st = DerbyMembershipHelper.Membership_UpdateUserInfo (connection, ApplicationName, username, isPasswordCorrect, updateLoginActivity,
\r
737 MaxInvalidPasswordAttempts, PasswordAttemptWindow, DateTime.UtcNow, pi.LastLoginDate, pi.LastActivityDate);
742 catch (Exception e) {
743 throw new ProviderException ("Failed to update Membership table", e);
749 PasswordInfo ValidateUsingPassword (string username, string password)
751 MembershipUser user = GetUser (username, true);
755 if (!user.IsApproved || user.IsLockedOut)
758 PasswordInfo pi = GetPasswordInfo (username);
763 /* do the actual validation */
764 string user_password = EncodePassword (password, pi.PasswordFormat, pi.PasswordSalt);
766 if (user_password != pi.Password) {
767 UpdateUserInfo (username, pi, false, false);
774 private PasswordInfo GetPasswordInfo (string username)
\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
780 PasswordInfo pi = null;
\r
781 if (reader == null)
\r
785 if (reader.Read ()) {
\r
786 int isLockedOut = reader.GetInt32 (1);
\r
787 if (isLockedOut > 0)
\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
805 private string EncodePassword (string password, MembershipPasswordFormat passwordFormat, string salt)
\r
807 byte [] password_bytes;
\r
808 byte [] salt_bytes;
\r
810 switch (passwordFormat) {
\r
811 case MembershipPasswordFormat.Clear:
\r
813 case MembershipPasswordFormat.Hashed:
\r
814 password_bytes = Encoding.Unicode.GetBytes (password);
\r
815 salt_bytes = Convert.FromBase64String (salt);
\r
817 byte [] hashBytes = new byte [salt_bytes.Length + password_bytes.Length];
\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
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
828 using (HashAlgorithm hash = HashAlgorithm.Create (alg_type)) {
\r
829 hash.TransformFinalBlock (hashBytes, 0, hashBytes.Length);
\r
830 return Convert.ToBase64String (hash.Hash);
\r
832 case MembershipPasswordFormat.Encrypted:
\r
833 password_bytes = Encoding.Unicode.GetBytes (password);
\r
834 salt_bytes = Convert.FromBase64String (salt);
\r
836 byte [] buf = new byte [password_bytes.Length + salt_bytes.Length];
\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
841 return Convert.ToBase64String (EncryptPassword (buf));
\r
843 /* not reached.. */
\r
848 private string DecodePassword (string password, MembershipPasswordFormat passwordFormat)
\r
850 switch (passwordFormat) {
\r
851 case MembershipPasswordFormat.Clear:
\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
858 /* not reached.. */
\r
863 public override string ApplicationName
865 get { return applicationName; }
866 set { applicationName = value; }
869 public override bool EnablePasswordReset
871 get { return enablePasswordReset; }
874 public override bool EnablePasswordRetrieval
876 get { return enablePasswordRetrieval; }
879 public override MembershipPasswordFormat PasswordFormat
881 get { return passwordFormat; }
884 public override bool RequiresQuestionAndAnswer
886 get { return requiresQuestionAndAnswer; }
889 public override bool RequiresUniqueEmail
891 get { return requiresUniqueEmail; }
894 public override int MaxInvalidPasswordAttempts
896 get { return maxInvalidPasswordAttempts; }
899 public override int MinRequiredNonAlphanumericCharacters
901 get { return minRequiredNonAlphanumericCharacters; }
904 public override int MinRequiredPasswordLength
906 get { return minRequiredPasswordLength; }
909 public override int PasswordAttemptWindow
911 get { return passwordAttemptWindow; }
914 public override string PasswordStrengthRegularExpression
916 get { return passwordStrengthRegularExpression; }
920 private enum DeleteUserTableMask
928 private sealed class PasswordInfo
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;
939 internal PasswordInfo (
941 MembershipPasswordFormat passwordFormat,
943 int failedPasswordAttemptCount,
944 int failedPasswordAnswerAttemptCount,
946 DateTime lastLoginDate,
947 DateTime lastActivityDate)
949 _password = password;
950 _passwordFormat = passwordFormat;
951 _passwordSalt = passwordSalt;
952 _failedPasswordAttemptCount = failedPasswordAttemptCount;
953 _failedPasswordAnswerAttemptCount = failedPasswordAnswerAttemptCount;
954 _isApproved = isApproved;
955 _lastLoginDate = lastLoginDate;
956 _lastActivityDate = lastActivityDate;
959 public string Password
961 get { return _password; }
962 set { _password = value; }
964 public MembershipPasswordFormat PasswordFormat
966 get { return _passwordFormat; }
967 set { _passwordFormat = value; }
969 public string PasswordSalt
971 get { return _passwordSalt; }
972 set { _passwordSalt = value; }
974 public int FailedPasswordAttemptCount
976 get { return _failedPasswordAttemptCount; }
977 set { _failedPasswordAttemptCount = value; }
979 public int FailedPasswordAnswerAttemptCount
981 get { return _failedPasswordAnswerAttemptCount; }
982 set { _failedPasswordAnswerAttemptCount = value; }
984 public bool IsApproved
986 get { return _isApproved; }
987 set { _isApproved = value; }
989 public DateTime LastLoginDate
991 get { return _lastLoginDate; }
992 set { _lastLoginDate = value; }
994 public DateTime LastActivityDate
996 get { return _lastActivityDate; }
997 set { _lastActivityDate = value; }