2 // Mainsoft.Web.Security.DerbyMembershipProvider
5 // Ben Maurer (bmaurer@users.sourceforge.net)
6 // Chris Toshok (toshok@ximian.com)
7 // Vladimir Krasnov (vladimirk@mainsoft.com)
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:
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
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.
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
;
42 using System
.Security
;
43 using System
.Security
.Cryptography
;
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
);
74 void CheckParam (string pName
, string p
, int length
)
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
);
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
);
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
);
124 string db_passwordAnswer
= EncodePassword (newPwdAnswer
, pi
.PasswordFormat
, pi
.PasswordSalt
);
126 int st
= DerbyMembershipHelper
.Membership_ChangePasswordQuestionAndAnswer (connection
, ApplicationName
, username
, newPwdQuestion
, newPwdAnswer
);
135 public override MembershipUser
CreateUser (string username
,
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
;
155 if (password
== null || password
.Length
== 0 || password
.Length
> 128) {
156 status
= MembershipCreateStatus
.InvalidPassword
;
160 if (!CheckPassword (password
)) {
161 status
= MembershipCreateStatus
.InvalidPassword
;
164 EmitValidatingPassword (username
, password
, true);
166 if (RequiresUniqueEmail
&& (email
== null || email
.Length
== 0)) {
167 status
= MembershipCreateStatus
.InvalidEmail
;
170 if (RequiresQuestionAndAnswer
&&
171 (pwdQuestion
== null ||
172 pwdQuestion
.Length
== 0 || pwdQuestion
.Length
> 256)) {
173 status
= MembershipCreateStatus
.InvalidQuestion
;
176 if (RequiresQuestionAndAnswer
&&
177 (pwdAnswer
== null ||
178 pwdAnswer
.Length
== 0 || pwdAnswer
.Length
> 128)) {
179 status
= MembershipCreateStatus
.InvalidAnswer
;
182 if (providerUserKey
!= null && !(providerUserKey
is string)) {
183 status
= MembershipCreateStatus
.InvalidProviderUserKey
;
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
];
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
;
207 if (RequiresQuestionAndAnswer
) {
208 if (pwdAnswer
.Length
> 128) {
209 status
= MembershipCreateStatus
.InvalidAnswer
;
213 status
= MembershipCreateStatus
.Success
;
215 using (DbConnection connection
= CreateConnection ()) {
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
);
224 return GetUser (providerUserKey
, false);
226 status
= MembershipCreateStatus
.DuplicateUserName
;
228 status
= MembershipCreateStatus
.DuplicateEmail
;
230 status
= MembershipCreateStatus
.InvalidProviderUserKey
;
232 status
= MembershipCreateStatus
.DuplicateProviderUserKey
;
234 status
= MembershipCreateStatus
.ProviderError
;
239 status
= MembershipCreateStatus
.ProviderError
;
245 private bool CheckPassword (string password
)
247 if (password
.Length
< MinRequiredPasswordLength
)
250 if (MinRequiredNonAlphanumericCharacters
> 0) {
251 int nonAlphanumeric
= 0;
252 for (int i
= 0; i
< password
.Length
; i
++) {
253 if (!Char
.IsLetterOrDigit (password
[i
]))
256 return nonAlphanumeric
>= MinRequiredNonAlphanumericCharacters
;
261 public override bool DeleteUser (string username
, bool deleteAllRelatedData
)
263 CheckParam ("username", username
, 256);
265 DeleteUserTableMask deleteBitmask
= DeleteUserTableMask
.MembershipUsers
;
267 if (deleteAllRelatedData
)
269 DeleteUserTableMask
.Profiles
|
270 DeleteUserTableMask
.UsersInRoles
|
271 DeleteUserTableMask
.WebPartStateUser
;
274 using (DbConnection connection
= CreateConnection ()) {
275 int st
= DerbyMembershipHelper
.Users_DeleteUser (connection
, ApplicationName
, username
, (int) deleteBitmask
, ref num
);
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);
297 throw new ArgumentException ("pageIndex must be >= 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");
304 using (DbConnection connection
= CreateConnection ()) {
305 DbDataReader reader
= null;
307 DerbyMembershipHelper
.Membership_FindUsersByEmail (connection
, ApplicationName
, emailToMatch
, pageSize
, pageIndex
, out 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);
322 throw new ArgumentException ("pageIndex must be >= 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");
329 using (DbConnection connection
= CreateConnection ()) {
330 DbDataReader reader
= null;
332 DerbyMembershipHelper
.Membership_FindUsersByName (connection
, ApplicationName
, nameToMatch
, pageSize
, pageIndex
, out reader
);
337 return BuildMembershipUserCollection (reader
, pageIndex
, pageSize
, out totalRecords
);
342 public override MembershipUserCollection
GetAllUsers (int pageIndex
, int pageSize
, out int totalRecords
)
345 throw new ArgumentException ("pageIndex must be >= 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
)
362 int num_to_skip
= pageIndex
* pageSize
;
363 MembershipUserCollection users
= new MembershipUserCollection ();
365 while (reader
.Read ()) {
366 if (num_read
>= num_to_skip
) {
367 if (num_added
< pageSize
) {
368 users
.Add (GetUserFromReader (reader
));
374 totalRecords
= num_read
;
379 return null; /* should we let the exception through? */
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
);
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
);
414 throw new ProviderException ("User specified by username is not found in the membership database");
417 throw new MembershipPasswordException ("The membership user identified by username is locked out");
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)
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
);
455 if (st
== 0 && reader
!= null) {
456 MembershipUser u
= GetUserFromReader (reader
);
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
);
476 if (st
== 0 && reader
!= null) {
477 MembershipUser u
= GetUserFromReader (reader
);
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
);
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");
503 bool GetBoolConfigValue (NameValueCollection config
, string name
, bool def
)
506 string val
= config
[name
];
508 try { rv = Boolean.Parse (val); }
509 catch (Exception e
) {
510 throw new ProviderException (String
.Format ("{0} must be true or false", name
), e
);
516 int GetIntConfigValue (NameValueCollection config
, string name
, int def
)
519 string val
= config
[name
];
521 try { rv = Int32.Parse (val); }
522 catch (Exception e
) {
523 throw new ProviderException (String
.Format ("{0} must be an integer", name
), e
);
529 int GetEnumConfigValue (NameValueCollection config
, string name
, Type enumType
, int def
)
532 string val
= config
[name
];
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
);
542 string GetStringConfigValue (NameValueCollection config
, string name
, string def
)
545 string val
= config
[name
];
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.. */
558 if (args
.FailureInformation
== null)
559 throw new ProviderException ("Password validation canceled");
561 throw args
.FailureInformation
;
565 public override void Initialize (string name
, NameValueCollection config
)
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
);
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
);
631 throw new ProviderException (username
+ " is not found in the membership database");
633 throw new MembershipPasswordException ("The user account is currently locked out");
635 throw new MembershipPasswordException ("Password Answer is invalid");
637 throw new ProviderException ("Failed to reset password");
641 public override void UpdateUser (MembershipUser user
)
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
);
661 throw new ProviderException ("The UserName property of user was not found in the database.");
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.");
665 throw new ProviderException ("Failed to update user");
669 public override bool ValidateUser (string username
, string password
)
671 if (username
.Length
== 0)
674 CheckParam ("username", username
, 256);
675 EmitValidatingPassword (username
, password
, false);
677 PasswordInfo pi
= ValidateUsingPassword (username
, password
);
679 pi
.LastLoginDate
= DateTime
.UtcNow
;
680 UpdateUserInfo (username
, pi
, true, true);
686 public override bool UnlockUser (string username
)
688 CheckParam ("username", username
, 256);
690 using (DbConnection connection
= CreateConnection ()) {
692 int st
= DerbyMembershipHelper
.Membership_UnlockUser (connection
, ApplicationName
, username
);
697 catch (Exception e
) {
698 throw new ProviderException ("Failed to unlock user", e
);
704 void UpdateUserInfo (string username
, PasswordInfo pi
, bool isPasswordCorrect
, bool updateLoginActivity
)
706 CheckParam ("username", username
, 256);
708 using (DbConnection connection
= CreateConnection ()) {
710 int st
= DerbyMembershipHelper
.Membership_UpdateUserInfo (connection
, ApplicationName
, username
, isPasswordCorrect
, updateLoginActivity
,
711 MaxInvalidPasswordAttempts
, PasswordAttemptWindow
, DateTime
.UtcNow
, pi
.LastLoginDate
, pi
.LastActivityDate
);
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);
729 if (!user
.IsApproved
|| user
.IsLockedOut
)
732 PasswordInfo pi
= GetPasswordInfo (username
);
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);
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;
759 if (reader
.Read ()) {
760 int isLockedOut
= reader
.GetInt32 (1);
764 pi
= new PasswordInfo (
765 reader
.GetString (3),
766 (MembershipPasswordFormat
) reader
.GetInt32 (4),
767 reader
.GetString (5),
770 reader
.GetInt32 (2) > 0,
771 reader
.GetDateTime (8),
772 reader
.GetDateTime (9));
779 private string EncodePassword (string password
, MembershipPasswordFormat passwordFormat
, string salt
)
781 byte [] password_bytes
;
784 switch (passwordFormat
) {
785 case MembershipPasswordFormat
.Clear
:
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
));
822 private string DecodePassword (string password
, MembershipPasswordFormat passwordFormat
)
824 switch (passwordFormat
) {
825 case MembershipPasswordFormat
.Clear
:
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
)));
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; }
894 private enum DeleteUserTableMask
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 (
915 MembershipPasswordFormat passwordFormat
,
917 int failedPasswordAttemptCount
,
918 int failedPasswordAnswerAttemptCount
,
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; }