PageRenderTime 90ms CodeModel.GetById 3ms app.highlight 76ms RepoModel.GetById 2ms app.codeStats 0ms

/mcs/class/System.Web/Test/standalone/sqlmembershipprovider/SqlMembershipProvider.cs

https://github.com/ztfuqingvip/mono
C# | 1795 lines | 1401 code | 311 blank | 83 comment | 234 complexity | f3af92b66ade79609aaf032f0fa6c2b8 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1//
   2// System.Web.Security.SqlMembershipProvider
   3//
   4// Authors:
   5//	Ben Maurer (bmaurer@users.sourceforge.net)
   6//	Lluis Sanchez Gual (lluis@novell.com)
   7//	Chris Toshok (toshok@ximian.com)
   8//
   9// (C) 2003 Ben Maurer
  10// Copyright (c) 2005,2006 Novell, Inc (http://www.novell.com)
  11//
  12// Permission is hereby granted, free of charge, to any person obtaining
  13// a copy of this software and associated documentation files (the
  14// "Software"), to deal in the Software without restriction, including
  15// without limitation the rights to use, copy, modify, merge, publish,
  16// distribute, sublicense, and/or sell copies of the Software, and to
  17// permit persons to whom the Software is furnished to do so, subject to
  18// the following conditions:
  19// 
  20// The above copyright notice and this permission notice shall be
  21// included in all copies or substantial portions of the Software.
  22// 
  23// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  24// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  25// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  26// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  27// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  28// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  29// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  30//
  31
  32#if NET_2_0
  33using System;
  34using System.Collections;
  35using System.Collections.Specialized;
  36using System.Configuration;
  37using System.Configuration.Provider;
  38using System.Data;
  39using System.Data.Common;
  40using System.Data.SqlClient;
  41using System.Text;
  42using System.Web.Configuration;
  43using System.Security.Cryptography;
  44using System.Web.Security;
  45
  46namespace Toshok.Web.Security {
  47	public class SqlMembershipProvider : MembershipProvider {
  48
  49		const int SALT_BYTES = 16;
  50		DateTime DefaultDateTime = new DateTime (1754,1,1);
  51
  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
  64		ConnectionStringSettings connectionString;
  65		DbProviderFactory factory;
  66		DbConnection connection;
  67
  68		string applicationName;
  69		
  70		static object lockobj = new object();
  71
  72                static byte ToHexValue (char c, bool high)
  73                {
  74                        byte v;
  75                        if (c >= '0' && c <= '9')
  76                                v = (byte) (c - '0');
  77                        else if (c >= 'a' && c <= 'f')
  78                                v = (byte) (c - 'a' + 10);
  79                        else if (c >= 'A' && c <= 'F')
  80                                v = (byte) (c - 'A' + 10);
  81                        else
  82                                throw new ArgumentException ("Invalid hex character");
  83
  84                        if (high)
  85                                v <<= 4;
  86
  87                        return v;
  88                }
  89
  90                internal static byte [] GetBytes (string key, int len)
  91                {
  92                        byte [] result = new byte [len / 2];
  93                        for (int i = 0; i < len; i += 2)
  94                                result [i / 2] = (byte) (ToHexValue (key [i], true) + ToHexValue (key [i + 1], false));
  95
  96                        return result;
  97                }
  98
  99		SymmetricAlgorithm GetAlg (out byte[] decryptionKey)
 100		{
 101			MachineKeySection section = (MachineKeySection)WebConfigurationManager.GetSection ("system.web/machineKey");
 102
 103			if (section.DecryptionKey.StartsWith ("AutoGenerate"))
 104				throw new ProviderException ("You must explicitly specify a decryption key in the <machineKey> section when using encrypted passwords.");
 105
 106			string alg_type = section.Decryption;
 107			if (alg_type == "Auto")
 108				alg_type = "AES";
 109
 110			SymmetricAlgorithm alg = null;
 111			if (alg_type == "AES")
 112				alg = Rijndael.Create ();
 113			else if (alg_type == "3DES")
 114				alg = TripleDES.Create ();
 115			else
 116				throw new ProviderException (String.Format ("Unsupported decryption attribute '{0}' in <machineKey> configuration section", alg_type));
 117
 118			decryptionKey = GetBytes (section.DecryptionKey, section.DecryptionKey.Length);
 119			return alg;
 120		}
 121
 122		internal string EncodePassword (string password, MembershipPasswordFormat passwordFormat, string salt)
 123		{
 124			byte[] password_bytes;
 125			byte[] salt_bytes;
 126
 127			switch (passwordFormat) {
 128			case MembershipPasswordFormat.Clear:
 129				return password;
 130			case MembershipPasswordFormat.Hashed:
 131				password_bytes = Encoding.Unicode.GetBytes (password);
 132				salt_bytes = Convert.FromBase64String (salt);
 133
 134				byte[] hashBytes = new byte[salt_bytes.Length + password_bytes.Length];
 135
 136				Buffer.BlockCopy (salt_bytes, 0, hashBytes, 0, salt_bytes.Length);
 137				Buffer.BlockCopy (password_bytes, 0, hashBytes, salt_bytes.Length, password_bytes.Length);
 138
 139				MembershipSection section = (MembershipSection)WebConfigurationManager.GetSection ("system.web/membership");
 140				string alg_type = section.HashAlgorithmType;
 141				if (alg_type == "") {
 142					MachineKeySection keysection = (MachineKeySection)WebConfigurationManager.GetSection ("system.web/machineKey");
 143					alg_type = keysection.Validation.ToString ();
 144				}
 145				using (HashAlgorithm hash = HashAlgorithm.Create (alg_type)) {
 146					hash.TransformFinalBlock (hashBytes, 0, hashBytes.Length);
 147					return Convert.ToBase64String (hash.Hash);
 148				}
 149			case MembershipPasswordFormat.Encrypted:
 150				password_bytes = Encoding.Unicode.GetBytes (password);
 151				salt_bytes = Convert.FromBase64String (salt);
 152
 153				byte[] buf = new byte[password_bytes.Length + salt_bytes.Length];
 154
 155				Array.Copy (salt_bytes, 0, buf, 0, salt_bytes.Length);
 156				Array.Copy (password_bytes, 0, buf, salt_bytes.Length, password_bytes.Length);
 157
 158				return Convert.ToBase64String (EncryptPassword (buf));
 159			default:
 160				/* not reached.. */
 161				return null;
 162			}
 163		}
 164
 165		internal string DecodePassword (string password, MembershipPasswordFormat passwordFormat)
 166		{
 167			switch (passwordFormat) {
 168			case MembershipPasswordFormat.Clear:
 169				return password;
 170			case MembershipPasswordFormat.Hashed:
 171				throw new ProviderException ("Hashed passwords cannot be decoded.");
 172			case MembershipPasswordFormat.Encrypted:
 173				return Encoding.Unicode.GetString (DecryptPassword (Convert.FromBase64String (password)));
 174			default:
 175				/* not reached.. */
 176				return null;
 177			}
 178		}
 179
 180		internal static DbProviderFactory GetDbProviderFactory (string providerName)
 181		{
 182			DbProviderFactory f = null;
 183
 184			if (providerName != null && providerName != "") {
 185				try {
 186					f = DbProviderFactories.GetFactory(providerName);
 187				}
 188				catch (Exception e) { Console.WriteLine (e); /* nada */ }
 189				if (f != null)
 190					return f;
 191			}
 192
 193			return SqlClientFactory.Instance;
 194		}
 195
 196		void InitConnection ()
 197		{
 198			if (connection == null) {
 199				lock (lockobj) {
 200					if (connection != null)
 201						return;
 202
 203					factory = GetDbProviderFactory (connectionString.ProviderName);
 204					connection = factory.CreateConnection();
 205					connection.ConnectionString = connectionString.ConnectionString;
 206
 207					connection.Open ();
 208				}
 209			}
 210		}
 211
 212		void AddParameter (DbCommand command, string parameterName, string parameterValue)
 213		{
 214			DbParameter dbp = command.CreateParameter ();
 215			dbp.ParameterName = parameterName;
 216			dbp.Value = parameterValue;
 217			dbp.Direction = ParameterDirection.Input;
 218			command.Parameters.Add (dbp);
 219		}
 220
 221		void CheckParam (string pName, string p, int length)
 222		{
 223			if (p == null)
 224				throw new ArgumentNullException (pName);
 225			if (p.Length == 0 || p.Length > length || p.IndexOf (",") != -1)
 226				throw new ArgumentException (String.Format ("invalid format for {0}", pName));
 227		}
 228
 229                protected override byte[] DecryptPassword (byte[] encodedPassword)
 230                {
 231			byte[] decryptionKey;
 232			SymmetricAlgorithm alg = GetAlg (out decryptionKey);
 233
 234			alg.Key = decryptionKey;
 235			ICryptoTransform decryptor = alg.CreateDecryptor ();
 236
 237			byte[] buf = decryptor.TransformFinalBlock (encodedPassword, 0, encodedPassword.Length);
 238			byte[] rv = new byte[buf.Length - SALT_BYTES];
 239
 240			Array.Copy (buf, 16, rv, 0, buf.Length - 16);
 241
 242			return rv;
 243                }
 244
 245                protected override byte[] EncryptPassword (byte[] password)
 246                {
 247			byte[] decryptionKey;
 248			byte[] iv = new byte[SALT_BYTES];
 249
 250			Array.Copy (password, 0, iv, 0, SALT_BYTES);
 251			Array.Clear (password, 0, SALT_BYTES);
 252
 253			SymmetricAlgorithm alg = GetAlg (out decryptionKey);
 254			ICryptoTransform encryptor = alg.CreateEncryptor (decryptionKey, iv);
 255
 256			return encryptor.TransformFinalBlock (password, 0, password.Length);
 257		}
 258
 259		public override bool ChangePassword (string username, string oldPwd, string newPwd)
 260		{
 261			if (username != null) username = username.Trim ();
 262			if (oldPwd != null) oldPwd = oldPwd.Trim ();
 263			if (newPwd != null) newPwd = newPwd.Trim ();
 264
 265			CheckParam ("username", username, 256);
 266			CheckParam ("oldPwd", oldPwd, 128);
 267			CheckParam ("newPwd", newPwd, 128);
 268
 269			MembershipUser user = GetUser (username, false);
 270			if (user == null) throw new ProviderException ("could not find user in membership database");
 271			if (user.IsLockedOut) throw new MembershipPasswordException ("user is currently locked out");
 272
 273			InitConnection();
 274
 275			DbTransaction trans = connection.BeginTransaction ();
 276
 277			string commandText;
 278			DbCommand command;
 279
 280			try {
 281				MembershipPasswordFormat passwordFormat;
 282				string db_salt;
 283
 284				bool valid = ValidateUsingPassword (trans, username, oldPwd, out passwordFormat, out db_salt);
 285				if (valid) {
 286
 287					EmitValidatingPassword (username, newPwd, false);
 288
 289					string db_password = EncodePassword (newPwd, passwordFormat, db_salt);
 290
 291					DateTime now = DateTime.Now.ToUniversalTime ();
 292
 293					commandText = @"
 294UPDATE m
 295   SET Password = @Password,
 296       FailedPasswordAttemptCount = 0,
 297       FailedPasswordAttemptWindowStart = @DefaultDateTime,
 298       LastPasswordChangedDate = @Now
 299  FROM dbo.aspnet_Membership m, dbo.aspnet_Users u, dbo.aspnet_Applications a
 300 WHERE m.ApplicationId = a.ApplicationId
 301   AND u.ApplicationId = a.ApplicationId
 302   AND m.UserId = u.UserId
 303   AND u.LoweredUserName = LOWER(@UserName)
 304   AND a.LoweredApplicationName = LOWER(@ApplicationName)";
 305
 306					command = factory.CreateCommand ();
 307					command.Transaction = trans;
 308					command.CommandText = commandText;
 309					command.Connection = connection;
 310					command.CommandType = CommandType.Text;
 311					AddParameter (command, "UserName", user.UserName);
 312					AddParameter (command, "Now", now.ToString ());
 313					AddParameter (command, "Password", db_password);
 314					AddParameter (command, "ApplicationName", ApplicationName);
 315					AddParameter (command, "DefaultDateTime", DefaultDateTime.ToString());
 316
 317					if (1 != (int)command.ExecuteNonQuery ())
 318						throw new ProviderException ("failed to update Membership table");
 319				}
 320
 321				trans.Commit ();
 322				return valid;
 323			}
 324			catch (ProviderException) {
 325				trans.Rollback ();
 326				throw;
 327			}
 328			catch (Exception e) {
 329				trans.Rollback ();
 330				throw new ProviderException ("error changing password", e);
 331			}
 332		}
 333		
 334		public override bool ChangePasswordQuestionAndAnswer (string username, string password, string newPwdQuestion, string newPwdAnswer)
 335		{
 336			if (username != null) username = username.Trim ();
 337			if (newPwdQuestion != null) newPwdQuestion = newPwdQuestion.Trim ();
 338			if (newPwdAnswer != null) newPwdAnswer = newPwdAnswer.Trim ();
 339
 340			CheckParam ("username", username, 256);
 341			if (RequiresQuestionAndAnswer)
 342				CheckParam ("newPwdQuestion", newPwdQuestion, 128);
 343			if (RequiresQuestionAndAnswer)
 344				CheckParam ("newPwdAnswer", newPwdAnswer, 128);
 345
 346			MembershipUser user = GetUser (username, false);
 347			if (user == null) throw new ProviderException ("could not find user in membership database");
 348			if (user.IsLockedOut) throw new MembershipPasswordException ("user is currently locked out");
 349
 350			InitConnection();
 351
 352			DbTransaction trans = connection.BeginTransaction ();
 353
 354			string commandText;
 355			DbCommand command;
 356
 357			try {
 358				MembershipPasswordFormat passwordFormat;
 359				string db_salt;
 360
 361				bool valid = ValidateUsingPassword (trans, username, password, out passwordFormat, out db_salt);
 362				if (valid) {
 363
 364					string db_passwordAnswer = EncodePassword (newPwdAnswer, passwordFormat, db_salt);
 365
 366					commandText = @"
 367UPDATE m
 368   SET PasswordQuestion = @PasswordQuestion,
 369       PasswordAnswer = @PasswordAnswer,
 370       FailedPasswordAttemptCount = 0,
 371       FailedPasswordAttemptWindowStart = @DefaultDateTime,
 372       FailedPasswordAnswerAttemptCount = 0,
 373       FailedPasswordAnswerAttemptWindowStart = @DefaultDateTime
 374  FROM dbo.aspnet_Membership m, dbo.aspnet_Users u, dbo.aspnet_Applications a
 375 WHERE m.ApplicationId = a.ApplicationId
 376   AND u.ApplicationId = a.ApplicationId
 377   AND m.UserId = u.UserId
 378   AND u.LoweredUserName = LOWER(@UserName)
 379   AND a.LoweredApplicationName = LOWER(@ApplicationName)";
 380
 381					command = factory.CreateCommand ();
 382					command.Transaction = trans;
 383					command.CommandText = commandText;
 384					command.Connection = connection;
 385					command.CommandType = CommandType.Text;
 386					AddParameter (command, "UserName", user.UserName);
 387					AddParameter (command, "PasswordQuestion", newPwdQuestion);
 388					AddParameter (command, "PasswordAnswer", db_passwordAnswer);
 389					AddParameter (command, "ApplicationName", ApplicationName);
 390					AddParameter (command, "DefaultDateTime", DefaultDateTime.ToString());
 391
 392					if (1 != (int)command.ExecuteNonQuery ())
 393						throw new ProviderException ("failed to update Membership table");
 394
 395				}
 396
 397				trans.Commit ();
 398				return valid;
 399			}
 400			catch (ProviderException) {
 401				trans.Rollback ();
 402				throw;
 403			}
 404			catch (Exception e) {
 405				trans.Rollback ();
 406				throw new ProviderException ("error changing password question and answer", e);
 407			}
 408		}
 409		
 410		public override MembershipUser CreateUser (string username,
 411							   string password,
 412							   string email,
 413							   string pwdQuestion,
 414							   string pwdAnswer,
 415							   bool isApproved,
 416							   object providerUserKey,
 417							   out MembershipCreateStatus status)
 418		{
 419			if (username != null) username = username.Trim ();
 420			if (password != null) password = password.Trim ();
 421			if (email != null) email = email.Trim ();
 422			if (pwdQuestion != null) pwdQuestion = pwdQuestion.Trim ();
 423			if (pwdAnswer != null) pwdAnswer = pwdAnswer.Trim ();
 424
 425			/* some initial validation */
 426			if (username == null || username.Length == 0 || username.Length > 256 || username.IndexOf (",") != -1) {
 427				status = MembershipCreateStatus.InvalidUserName;
 428				return null;
 429			}
 430			if (password == null || password.Length == 0 || password.Length > 128) {
 431				status = MembershipCreateStatus.InvalidPassword;
 432				return null;
 433			}
 434			if (RequiresUniqueEmail && (email == null || email.Length == 0)) {
 435				status = MembershipCreateStatus.InvalidEmail;
 436				return null;
 437			}
 438			if (RequiresQuestionAndAnswer &&
 439			    (pwdQuestion == null ||
 440			     pwdQuestion.Length == 0 || pwdQuestion.Length > 256)) {
 441				status = MembershipCreateStatus.InvalidQuestion;
 442				return null;
 443			}
 444			if (RequiresQuestionAndAnswer &&
 445			    (pwdAnswer == null ||
 446			     pwdAnswer.Length == 0 || pwdAnswer.Length > 128)) {
 447				status = MembershipCreateStatus.InvalidAnswer;
 448				return null;
 449			}
 450			if (providerUserKey != null && ! (providerUserKey is Guid)) {
 451				status = MembershipCreateStatus.InvalidProviderUserKey;
 452				return null;
 453			}
 454
 455			/* encode our password/answer using the
 456			 * "passwordFormat" configuration option */
 457			string passwordSalt = "";
 458
 459			RandomNumberGenerator rng = RandomNumberGenerator.Create ();
 460			byte[] salt = new byte[SALT_BYTES];
 461			rng.GetBytes (salt);
 462			passwordSalt = Convert.ToBase64String (salt);
 463
 464			password = EncodePassword (password, PasswordFormat, passwordSalt);
 465			if (RequiresQuestionAndAnswer)
 466				pwdAnswer = EncodePassword (pwdAnswer, PasswordFormat, passwordSalt);
 467
 468			/* make sure the hashed/encrypted password and
 469			 * answer are still under 128 characters. */
 470			if (password.Length > 128) {
 471				status = MembershipCreateStatus.InvalidPassword;
 472				return null;
 473			}
 474
 475			if (RequiresQuestionAndAnswer) {
 476				if (pwdAnswer.Length > 128) {
 477					status = MembershipCreateStatus.InvalidAnswer;
 478					return null;
 479				}
 480			}
 481
 482			InitConnection();
 483
 484			DbTransaction trans = connection.BeginTransaction ();
 485
 486			string commandText;
 487			DbCommand command;
 488
 489			try {
 490
 491				Guid applicationId;
 492				Guid userId;
 493
 494				/* get the application id since it seems that inside transactions we
 495				   can't insert using subqueries.. */
 496
 497				commandText = @"
 498SELECT ApplicationId
 499  FROM dbo.aspnet_Applications
 500 WHERE dbo.aspnet_Applications.LoweredApplicationName = LOWER(@ApplicationName)
 501";
 502				command = factory.CreateCommand ();
 503				command.Transaction = trans;
 504				command.CommandText = commandText;
 505				command.Connection = connection;
 506				command.CommandType = CommandType.Text;
 507				AddParameter (command, "ApplicationName", ApplicationName);
 508
 509				DbDataReader reader = command.ExecuteReader ();
 510				reader.Read ();
 511				applicationId = reader.GetGuid (0);
 512				reader.Close ();
 513
 514				/* check for unique username, email and
 515				 * provider user key, if applicable */
 516
 517				commandText = @"
 518SELECT COUNT(*)
 519  FROM dbo.aspnet_Users u, dbo.aspnet_Applications a
 520 WHERE u.LoweredUserName = LOWER(@UserName)
 521   AND u.ApplicationId = a.ApplicationId
 522   AND a.LoweredApplicationName = LOWER(@ApplicationName)";
 523
 524				command = factory.CreateCommand ();
 525				command.Transaction = trans;
 526				command.CommandText = commandText;
 527				command.Connection = connection;
 528				command.CommandType = CommandType.Text;
 529				AddParameter (command, "UserName", username);
 530				AddParameter (command, "ApplicationName", ApplicationName);
 531
 532				if (0 != (int)command.ExecuteScalar()) {
 533					status = MembershipCreateStatus.DuplicateUserName;
 534					trans.Rollback ();
 535					return null;
 536				}
 537
 538
 539				if (requiresUniqueEmail) {
 540					commandText = @"
 541SELECT COUNT(*)
 542  FROM dbo.aspnet_Membership, dbo.aspnet_Applications
 543 WHERE dbo.aspnet_Membership.Email = @Email
 544   AND dbo.aspnet_Membership.ApplicationId = dbo.aspnet_Applications.ApplicationId
 545   AND dbo.aspnet_Applications.LoweredApplicationName = LOWER(@ApplicationName)";
 546
 547					command = factory.CreateCommand ();
 548					command.Transaction = trans;
 549					command.CommandText = commandText;
 550					command.Connection = connection;
 551					command.CommandType = CommandType.Text;
 552					AddParameter (command, "Email", email);
 553					AddParameter (command, "ApplicationName", ApplicationName);
 554
 555					if (0 != (int)command.ExecuteScalar()) {
 556						status = MembershipCreateStatus.DuplicateEmail;
 557						trans.Rollback ();
 558						return null;
 559					}
 560		 		}
 561
 562				if (providerUserKey != null) {
 563					commandText = @"
 564SELECT COUNT(*)
 565  FROM dbo.aspnet_Membership, dbo.aspnet_Applications
 566 WHERE dbo.aspnet_Membership.UserId = @ProviderUserKey
 567   AND dbo.aspnet_Membership.ApplicationId = dbo.aspnet_Applications.ApplicationId
 568   AND dbo.aspnet_Applications.LoweredApplicationName = LOWER(@ApplicationName)";
 569
 570					command = factory.CreateCommand ();
 571					command.Transaction = trans;
 572					command.CommandText = commandText;
 573					command.Connection = connection;
 574					command.CommandType = CommandType.Text;
 575					AddParameter (command, "Email", email);
 576					AddParameter (command, "ApplicationName", ApplicationName);
 577
 578					if (0 != (int)command.ExecuteScalar()) {
 579						status = MembershipCreateStatus.DuplicateProviderUserKey;
 580						trans.Rollback ();
 581						return null;
 582					}
 583				}
 584
 585				/* first into the Users table */
 586				commandText = @"
 587INSERT into dbo.aspnet_Users (ApplicationId, UserId, UserName, LoweredUserName, LastActivityDate)
 588VALUES (@ApplicationId, NEWID(), @UserName, LOWER(@UserName), GETDATE())
 589";
 590
 591				command = factory.CreateCommand ();
 592				command.Transaction = trans;
 593				command.CommandText = commandText;
 594				command.Connection = connection;
 595				command.CommandType = CommandType.Text;
 596				AddParameter (command, "UserName", username);
 597				AddParameter (command, "ApplicationId", applicationId.ToString());
 598
 599				if (command.ExecuteNonQuery() != 1) {
 600					status = MembershipCreateStatus.UserRejected; /* XXX */
 601					trans.Rollback ();
 602					return null;
 603				}
 604
 605				/* then get the newly created userid */
 606
 607				commandText = @"
 608SELECT UserId
 609  FROM dbo.aspnet_Users
 610 WHERE dbo.aspnet_Users.LoweredUserName = LOWER(@UserName)
 611";
 612				command = factory.CreateCommand ();
 613				command.Transaction = trans;
 614				command.CommandText = commandText;
 615				command.Connection = connection;
 616				command.CommandType = CommandType.Text;
 617				AddParameter (command, "UserName", username);
 618
 619				reader = command.ExecuteReader ();
 620				reader.Read ();
 621				userId = reader.GetGuid (0);
 622				reader.Close ();
 623
 624				/* then insert into the Membership table */
 625				commandText = String.Format (@"
 626INSERT into dbo.aspnet_Membership
 627VALUES (@ApplicationId,
 628        @UserId,
 629        @Password, @PasswordFormat, @PasswordSalt,
 630        NULL,
 631        {0}, {1},
 632        {2}, {3},
 633        0, 0,
 634        GETDATE(), GETDATE(), @DefaultDateTime,
 635        @DefaultDateTime,
 636        0, @DefaultDateTime, 0, @DefaultDateTime, NULL)",
 637							     email == null ? "NULL" : "@Email",
 638							     email == null ? "NULL" : "LOWER(@Email)",
 639							     pwdQuestion == null ? "NULL" : "@PasswordQuestion",
 640							     pwdAnswer == null ? "NULL" : "@PasswordAnswer");
 641
 642				command = factory.CreateCommand ();
 643				command.Transaction = trans;
 644				command.CommandText = commandText;
 645				command.Connection = connection;
 646				command.CommandType = CommandType.Text;
 647				AddParameter (command, "ApplicationId", applicationId.ToString());
 648				AddParameter (command, "UserId", userId.ToString());
 649				if (email != null)
 650					AddParameter (command, "Email", email);
 651				AddParameter (command, "Password", password);
 652				AddParameter (command, "PasswordFormat", ((int)PasswordFormat).ToString());
 653				AddParameter (command, "PasswordSalt", passwordSalt);
 654				if (pwdQuestion != null)
 655					AddParameter (command, "PasswordQuestion", pwdQuestion);
 656				if (pwdAnswer != null)
 657					AddParameter (command, "PasswordAnswer", pwdAnswer);
 658				AddParameter (command, "DefaultDateTime", DefaultDateTime.ToString());
 659
 660				if (command.ExecuteNonQuery() != 1) {
 661					status = MembershipCreateStatus.UserRejected; /* XXX */
 662					return null;
 663				}
 664
 665				trans.Commit ();
 666
 667				status = MembershipCreateStatus.Success;
 668
 669				return GetUser (username, false);
 670			}
 671			catch {
 672				status = MembershipCreateStatus.ProviderError;
 673				trans.Rollback ();
 674				return null;
 675			}
 676		}
 677		
 678		public override bool DeleteUser (string username, bool deleteAllRelatedData)
 679		{
 680			CheckParam ("username", username, 256);
 681
 682			if (deleteAllRelatedData) {
 683				/* delete everything from the
 684				 * following features as well:
 685				 *
 686				 * Roles
 687				 * Profile
 688				 * WebParts Personalization
 689				 */
 690			}
 691
 692			DbTransaction trans = connection.BeginTransaction ();
 693
 694			DbCommand command;
 695			string commandText;
 696
 697			InitConnection();
 698
 699			try {
 700				/* delete from the Membership table */
 701				commandText = @"
 702DELETE dbo.aspnet_Membership
 703  FROM dbo.aspnet_Membership, dbo.aspnet_Users, dbo.aspnet_Applications
 704 WHERE dbo.aspnet_Membership.UserId = dbo.aspnet_Users.UserId
 705   AND dbo.aspnet_Membership.ApplicationId = dbo.aspnet_Applications.ApplicationId
 706   AND dbo.aspnet_Users.LoweredUserName = LOWER (@UserName)
 707   AND dbo.aspnet_Users.ApplicationId = dbo.aspnet_Applications.ApplicationId
 708   AND dbo.aspnet_Applications.LoweredApplicationName = LOWER(@ApplicationName)";
 709
 710				command = factory.CreateCommand ();
 711				command.Transaction = trans;
 712				command.CommandText = commandText;
 713				command.Connection = connection;
 714				command.CommandType = CommandType.Text;
 715				AddParameter (command, "UserName", username);
 716				AddParameter (command, "ApplicationName", ApplicationName);
 717
 718				if (1 != command.ExecuteNonQuery())
 719					throw new ProviderException ("failed to delete from Membership table");
 720
 721				/* delete from the User table */
 722				commandText = @"
 723DELETE dbo.aspnet_Users
 724  FROM dbo.aspnet_Users, dbo.aspnet_Applications
 725 WHERE dbo.aspnet_Users.LoweredUserName = LOWER (@UserName)
 726   AND dbo.aspnet_Users.ApplicationId = dbo.aspnet_Applications.ApplicationId
 727   AND dbo.aspnet_Applications.LoweredApplicationName = LOWER(@ApplicationName)";
 728
 729				command = factory.CreateCommand ();
 730				command.Transaction = trans;
 731				command.CommandText = commandText;
 732				command.Connection = connection;
 733				command.CommandType = CommandType.Text;
 734				AddParameter (command, "UserName", username);
 735				AddParameter (command, "ApplicationName", ApplicationName);
 736
 737				if (1 != command.ExecuteNonQuery())
 738					throw new ProviderException ("failed to delete from User table");
 739
 740				trans.Commit ();
 741
 742				return true;
 743			}
 744			catch {
 745				trans.Rollback ();
 746				return false;
 747			}
 748		}
 749		
 750		public virtual string GeneratePassword ()
 751		{
 752			return Membership.GeneratePassword (minRequiredPasswordLength, minRequiredNonAlphanumericCharacters);
 753		}
 754		
 755		public override MembershipUserCollection FindUsersByEmail (string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
 756		{
 757			CheckParam ("emailToMatch", emailToMatch, 256);
 758
 759			if (pageIndex < 0)
 760				throw new ArgumentException ("pageIndex must be >= 0");
 761			if (pageSize < 0)
 762				throw new ArgumentException ("pageSize must be >= 0");
 763			if (pageIndex * pageSize + pageSize - 1 > Int32.MaxValue)
 764				throw new ArgumentException ("pageIndex and pageSize are too large");
 765
 766			string commandText;
 767
 768			InitConnection();
 769
 770			commandText = @"
 771SELECT u.UserName, m.UserId, m.Email, m.PasswordQuestion, m.Comment, m.IsApproved,
 772       m.IsLockedOut, m.CreateDate, m.LastLoginDate, u.LastActivityDate,
 773       m.LastPasswordChangedDate, m.LastLockoutDate
 774  FROM dbo.aspnet_Membership m, dbo.aspnet_Applications a, dbo.aspnet_Users u
 775 WHERE m.ApplicationId = a.ApplicationId
 776   AND u.ApplicationId = a.ApplicationId
 777   AND m.UserId = u.UserId
 778   AND m.Email LIKE @Email
 779   AND a.LoweredApplicationName = LOWER(@ApplicationName)";
 780
 781			DbCommand command = factory.CreateCommand ();
 782			command.CommandText = commandText;
 783			command.Connection = connection;
 784			command.CommandType = CommandType.Text;
 785			AddParameter (command, "Email", emailToMatch);
 786			AddParameter (command, "ApplicationName", ApplicationName);
 787
 788			MembershipUserCollection c = BuildMembershipUserCollection (command, pageIndex, pageSize, out totalRecords);
 789
 790			return c;
 791		}
 792
 793		public override MembershipUserCollection FindUsersByName (string nameToMatch, int pageIndex, int pageSize, out int totalRecords)
 794		{
 795			CheckParam ("nameToMatch", nameToMatch, 256);
 796
 797			if (pageIndex < 0)
 798				throw new ArgumentException ("pageIndex must be >= 0");
 799			if (pageSize < 0)
 800				throw new ArgumentException ("pageSize must be >= 0");
 801			if (pageIndex * pageSize + pageSize - 1 > Int32.MaxValue)
 802				throw new ArgumentException ("pageIndex and pageSize are too large");
 803
 804			string commandText;
 805
 806			InitConnection();
 807
 808			commandText = @"
 809SELECT u.UserName, m.UserId, m.Email, m.PasswordQuestion, m.Comment, m.IsApproved,
 810       m.IsLockedOut, m.CreateDate, m.LastLoginDate, u.LastActivityDate,
 811       m.LastPasswordChangedDate, m.LastLockoutDate
 812  FROM dbo.aspnet_Membership m, dbo.aspnet_Applications a, dbo.aspnet_Users u
 813 WHERE m.ApplicationId = a.ApplicationId
 814   AND u.ApplicationId = a.ApplicationId
 815   AND m.UserId = u.UserId
 816   AND u.UserName LIKE @UserName
 817   AND a.LoweredApplicationName = LOWER(@ApplicationName)";
 818
 819			DbCommand command = factory.CreateCommand ();
 820			command.CommandText = commandText;
 821			command.Connection = connection;
 822			command.CommandType = CommandType.Text;
 823			AddParameter (command, "UserName", nameToMatch);
 824			AddParameter (command, "ApplicationName", ApplicationName);
 825
 826			MembershipUserCollection c = BuildMembershipUserCollection (command, pageIndex, pageSize, out totalRecords);
 827
 828			return c;
 829		}
 830		
 831		public override MembershipUserCollection GetAllUsers (int pageIndex, int pageSize, out int totalRecords)
 832		{
 833			if (pageIndex < 0)
 834				throw new ArgumentException ("pageIndex must be >= 0");
 835			if (pageSize < 0)
 836				throw new ArgumentException ("pageSize must be >= 0");
 837			if (pageIndex * pageSize + pageSize - 1 > Int32.MaxValue)
 838				throw new ArgumentException ("pageIndex and pageSize are too large");
 839
 840			string commandText;
 841
 842			InitConnection();
 843
 844			commandText = @"
 845SELECT u.UserName, m.UserId, m.Email, m.PasswordQuestion, m.Comment, m.IsApproved,
 846       m.IsLockedOut, m.CreateDate, m.LastLoginDate, u.LastActivityDate,
 847       m.LastPasswordChangedDate, m.LastLockoutDate
 848  FROM dbo.aspnet_Membership m, dbo.aspnet_Applications a, dbo.aspnet_Users u
 849 WHERE m.ApplicationId = a.ApplicationId
 850   AND u.ApplicationId = a.ApplicationId
 851   AND m.UserId = u.UserId
 852   AND a.LoweredApplicationName = LOWER(@ApplicationName)";
 853
 854			DbCommand command = factory.CreateCommand ();
 855			command.CommandText = commandText;
 856			command.Connection = connection;
 857			command.CommandType = CommandType.Text;
 858			AddParameter (command, "ApplicationName", ApplicationName);
 859
 860			MembershipUserCollection c = BuildMembershipUserCollection (command, pageIndex, pageSize, out totalRecords);
 861
 862			return c;
 863		}
 864
 865		MembershipUserCollection BuildMembershipUserCollection (DbCommand command, int pageIndex, int pageSize, out int totalRecords)
 866		{
 867			DbDataReader reader = null;
 868			try {
 869				int num_read = 0;
 870				int num_added = 0;
 871				int num_to_skip = pageIndex * pageSize;
 872				MembershipUserCollection users = new MembershipUserCollection ();
 873				reader = command.ExecuteReader ();
 874				while (reader.Read()) {
 875					if (num_read >= num_to_skip) {
 876						if (num_added < pageSize) {
 877							users.Add (GetUserFromReader (reader));
 878							num_added ++;
 879						}
 880						num_read ++;
 881					}
 882				}
 883				totalRecords = num_read;
 884				return users;
 885			}
 886			catch {
 887				totalRecords = 0;
 888				return null; /* should we let the exception through? */
 889			}
 890			finally {
 891				if (reader != null)
 892					reader.Close();
 893			}
 894		}
 895		
 896		
 897		public override int GetNumberOfUsersOnline ()
 898		{
 899			string commandText;
 900
 901			InitConnection();
 902
 903			DateTime now = DateTime.Now.ToUniversalTime ();
 904
 905			commandText = String.Format (@"
 906SELECT COUNT (*)
 907  FROM dbo.aspnet_Membership m, dbo.aspnet_Applications a, dbo.aspnet_Users u
 908 WHERE m.ApplicationId = a.ApplicationId
 909   AND u.ApplicationId = a.ApplicationId
 910   AND m.UserId = u.UserId
 911   AND DATEADD(minute,{0},u.LastActivityDate) >= @Now
 912   AND a.LoweredApplicationName = LOWER(@ApplicationName)",
 913						     userIsOnlineTimeWindow.Minutes);
 914
 915			DbCommand command = factory.CreateCommand ();
 916			command.CommandText = commandText;
 917			command.Connection = connection;
 918			command.CommandType = CommandType.Text;
 919			AddParameter (command, "Now", now.ToString ());
 920			AddParameter (command, "ApplicationName", ApplicationName);
 921
 922			try { 
 923				return (int)command.ExecuteScalar ();
 924			}
 925			catch (Exception e) {
 926				Console.WriteLine (e);
 927				return -1;
 928			}
 929		}
 930		
 931		public override string GetPassword (string username, string answer)
 932		{
 933			if (!enablePasswordRetrieval)
 934				throw new NotSupportedException ("this provider has not been configured to allow the retrieval of passwords");
 935
 936			CheckParam ("username", username, 256);
 937			if (RequiresQuestionAndAnswer)
 938				CheckParam ("answer", answer, 128);
 939
 940			MembershipUser user = GetUser (username, false);
 941			if (user == null) throw new ProviderException ("could not find user in membership database");
 942			if (user.IsLockedOut) throw new MembershipPasswordException ("user is currently locked out");
 943
 944			InitConnection();
 945
 946			DbTransaction trans = connection.BeginTransaction ();
 947
 948			try {
 949				MembershipPasswordFormat passwordFormat;
 950				string salt;
 951				string password = null;
 952
 953				if (ValidateUsingPasswordAnswer (trans, username, answer,
 954								 out passwordFormat, out salt)) {
 955
 956
 957					/* if the validation succeeds:
 958
 959					   set LastLoginDate to DateTime.Now
 960					   set FailedPasswordAnswerAttemptCount to 0
 961					   set FailedPasswordAnswerAttemptWindowStart to DefaultDateTime
 962					*/
 963
 964					string commandText = @"
 965SELECT m.Password
 966  FROM dbo.aspnet_Membership m, dbo.aspnet_Applications a, dbo.aspnet_Users u
 967 WHERE m.ApplicationId = a.ApplicationId
 968   AND u.ApplicationId = a.ApplicationId
 969   AND m.UserId = u.UserId
 970   AND u.LoweredUserName = LOWER(@UserName)
 971   AND a.LoweredApplicationName = LOWER(@ApplicationName)";
 972
 973					DbCommand command = factory.CreateCommand ();
 974					command.Transaction = trans;
 975					command.CommandText = commandText;
 976					command.Connection = connection;
 977					command.CommandType = CommandType.Text;
 978					AddParameter (command, "UserName", username);
 979					AddParameter (command, "ApplicationName", ApplicationName);
 980
 981					DbDataReader reader = command.ExecuteReader ();
 982					reader.Read ();
 983					password = reader.GetString (0);
 984					reader.Close();
 985
 986					password = DecodePassword (password, passwordFormat);
 987				}
 988				else {
 989					throw new MembershipPasswordException ("The password-answer supplied is wrong.");
 990				}
 991
 992				trans.Commit ();
 993				return password;
 994			}
 995			catch (MembershipPasswordException) {
 996				trans.Commit ();
 997				throw;
 998			}
 999			catch {
1000				trans.Rollback ();
1001				throw;
1002			}
1003		}
1004
1005		MembershipUser GetUserFromReader (DbDataReader reader)
1006		{
1007			return new MembershipUser (this.Name, /* XXX is this right?  */
1008						   reader.GetString (0), /* name */
1009						   reader.GetGuid (1), /* providerUserKey */
1010						   reader.IsDBNull (2) ? null : reader.GetString (2), /* email */
1011						   reader.IsDBNull (3) ? null : reader.GetString (3), /* passwordQuestion */
1012						   reader.IsDBNull (4) ? null : reader.GetString (4), /* comment */
1013						   reader.GetBoolean (5), /* isApproved */
1014						   reader.GetBoolean (6), /* isLockedOut */
1015						   reader.GetDateTime (7).ToLocalTime (), /* creationDate */
1016						   reader.GetDateTime (8).ToLocalTime (), /* lastLoginDate */
1017						   reader.GetDateTime (9).ToLocalTime (), /* lastActivityDate */
1018						   reader.GetDateTime (10).ToLocalTime (), /* lastPasswordChangedDate */
1019						   reader.GetDateTime (11).ToLocalTime () /* lastLockoutDate */);
1020		}
1021
1022		MembershipUser BuildMembershipUser (DbCommand query, bool userIsOnline)
1023		{
1024			DbDataReader reader = null;
1025			try {
1026				reader = query.ExecuteReader ();
1027				if (!reader.Read ())
1028					return null;
1029
1030				MembershipUser user = GetUserFromReader (reader);
1031
1032				if (user != null && userIsOnline) {
1033
1034					string commandText;
1035					DbCommand command;
1036
1037					commandText = @"
1038UPDATE dbo.aspnet_Users u, dbo.aspnet_Application a
1039   SET u.LastActivityDate = GETDATE()
1040 WHERE u.ApplicationId = a.ApplicationId
1041   AND u.UserName = @UserName
1042   AND a.LoweredApplicationName = LOWER(@ApplicationName)";
1043
1044					command = factory.CreateCommand ();
1045					command.CommandText = commandText;
1046					command.Connection = connection;
1047					command.CommandType = CommandType.Text;
1048					AddParameter (command, "UserName", user.UserName);
1049					AddParameter (command, "ApplicationName", ApplicationName);
1050
1051					command.ExecuteNonQuery();
1052				}
1053
1054				return user;
1055			}
1056			catch {
1057				return null; /* should we let the exception through? */
1058			}
1059			finally {
1060				if (reader != null)
1061					reader.Close ();
1062			}
1063		}
1064
1065		public override MembershipUser GetUser (string username, bool userIsOnline)
1066		{
1067			CheckParam ("username", username, 256);
1068
1069			string commandText;
1070			DbCommand command;
1071
1072			InitConnection();
1073
1074			commandText = @"
1075SELECT u.UserName, m.UserId, m.Email, m.PasswordQuestion, m.Comment, m.IsApproved,
1076       m.IsLockedOut, m.CreateDate, m.LastLoginDate, u.LastActivityDate,
1077       m.LastPasswordChangedDate, m.LastLockoutDate
1078  FROM dbo.aspnet_Membership m, dbo.aspnet_Applications a, dbo.aspnet_Users u
1079 WHERE m.ApplicationId = a.ApplicationId
1080   AND u.ApplicationId = a.ApplicationId
1081   AND m.UserId = u.UserId
1082   AND u.UserName = @UserName
1083   AND a.LoweredApplicationName = LOWER(@ApplicationName)";
1084
1085			command = factory.CreateCommand ();
1086			command.CommandText = commandText;
1087			command.Connection = connection;
1088			command.CommandType = CommandType.Text;
1089			AddParameter (command, "UserName", username);
1090			AddParameter (command, "ApplicationName", ApplicationName);
1091
1092			MembershipUser u = BuildMembershipUser (command, userIsOnline);
1093
1094			return u;
1095		}
1096		
1097		public override MembershipUser GetUser (object providerUserKey, bool userIsOnline)
1098		{
1099			string commandText;
1100			DbCommand command;
1101
1102			InitConnection();
1103
1104			commandText = @"
1105SELECT u.UserName, m.UserId, m.Email, m.PasswordQuestion, m.Comment, m.IsApproved,
1106       m.IsLockedOut, m.CreateDate, m.LastLoginDate, u.LastActivityDate,
1107       m.LastPasswordChangedDate, m.LastLockoutDate
1108  FROM dbo.aspnet_Membership m, dbo.aspnet_Applications a, dbo.aspnet_Users u
1109 WHERE m.ApplicationId = a.ApplicationId
1110   AND u.ApplicationId = a.ApplicationId
1111   AND m.UserId = u.UserId
1112   AND u.UserId = @UserKey
1113   AND a.LoweredApplicationName = LOWER(@ApplicationName)";
1114
1115			command = factory.CreateCommand ();
1116			command.CommandText = commandText;
1117			command.Connection = connection;
1118			command.CommandType = CommandType.Text;
1119			AddParameter (command, "UserKey", providerUserKey.ToString());
1120			AddParameter (command, "ApplicationName", ApplicationName);
1121
1122			MembershipUser u = BuildMembershipUser (command, userIsOnline);
1123
1124			return u;
1125		}
1126		
1127		public override string GetUserNameByEmail (string email)
1128		{
1129			CheckParam ("email", email, 256);
1130
1131			string commandText;
1132			DbCommand command;
1133
1134			InitConnection();
1135
1136			commandText = @"
1137SELECT u.UserName
1138  FROM dbo.aspnet_Membership m, dbo.aspnet_Applications a, dbo.aspnet_Users u
1139 WHERE m.ApplicationId = a.ApplicationId
1140   AND u.ApplicationId = a.ApplicationId
1141   AND m.UserId = u.UserId
1142   AND m.Email = @Email
1143   AND a.LoweredApplicationName = LOWER(@ApplicationName)";
1144
1145			command = factory.CreateCommand ();
1146			command.CommandText = commandText;
1147			command.Connection = connection;
1148			command.CommandType = CommandType.Text;
1149			AddParameter (command, "Email", email);
1150			AddParameter (command, "ApplicationName", ApplicationName);
1151
1152			try {
1153				DbDataReader reader = command.ExecuteReader ();
1154				string rv = null;
1155				while (reader.Read())
1156					rv = reader.GetString(0);
1157				reader.Close();
1158				return rv;
1159			}
1160			catch {
1161				return null; /* should we allow the exception through? */
1162			}
1163		}
1164
1165		bool GetBoolConfigValue (NameValueCollection config, string name, bool def)
1166		{
1167			bool rv = def;
1168			string val = config[name];
1169			if (val != null) {
1170				try { rv = Boolean.Parse (val); }
1171				catch (Exception e) {
1172					throw new ProviderException (String.Format ("{0} must be true or false", name), e); }
1173			}
1174			return rv;
1175		}
1176
1177		int GetIntConfigValue (NameValueCollection config, string name, int def)
1178		{
1179			int rv = def;
1180			string val = config[name];
1181			if (val != null) {
1182				try { rv = Int32.Parse (val); }
1183				catch (Exception e) {
1184					throw new ProviderException (String.Format ("{0} must be an integer", name), e); }
1185			}
1186			return rv;
1187		}
1188
1189		int GetEnumConfigValue (NameValueCollection config, string name, Type enumType, int def)
1190		{
1191			int rv = def;
1192			string val = config[name];
1193			if (val != null) {
1194				try { rv = (int)Enum.Parse (enumType, val); }
1195				catch (Exception e) {
1196					throw new ProviderException (String.Format ("{0} must be one of the following values: {1}", name, String.Join (",", Enum.GetNames (enumType))), e); }
1197			}
1198			return rv;
1199		}
1200
1201		string GetStringConfigValue (NameValueCollection config, string name, string def)
1202		{
1203			string rv = def;
1204			string val = config[name];
1205			if (val != null)
1206				rv = val;
1207			return rv;
1208		}
1209
1210		void EmitValidatingPassword (string username, string password, bool isNewUser)
1211		{
1212			ValidatePasswordEventArgs args = new ValidatePasswordEventArgs (username, password, isNewUser);
1213			OnValidatingPassword (args);
1214
1215			/* if we're canceled.. */
1216			if (args.Cancel) {
1217				if (args.FailureInformation == null)
1218					throw new ProviderException ("Password validation canceled");
1219				else
1220					throw args.FailureInformation;
1221			}
1222		}
1223
1224		public override void Initialize (string name, NameValueCollection config)
1225		{
1226			if (config == null)
1227				throw new ArgumentNullException ("config");
1228
1229			base.Initialize (name, config);
1230
1231			applicationName = GetStringConfigValue (config, "applicationName", "/");
1232			enablePasswordReset = GetBoolConfigValue (config, "enablePasswordReset", true);
1233			enablePasswordRetrieval = GetBoolConfigValue (config, "enablePasswordRetrieval", false);
1234			requiresQuestionAndAnswer = GetBoolConfigValue (config, "requiresQuestionAndAnswer", true);
1235			requiresUniqueEmail = GetBoolConfigValue (config, "requiresUniqueEmail", false);
1236			passwordFormat = (MembershipPasswordFormat)GetEnumConfigValue (config, "passwordFormat", typeof (MembershipPasswordFormat),
1237										       (int)MembershipPasswordFormat.Hashed);
1238			maxInvalidPasswordAttempts = GetIntConfigValue (config, "maxInvalidPasswordAttempts", 5);
1239			minRequiredPasswordLength = GetIntConfigValue (config, "minRequiredPasswordLength", 7);
1240			minRequiredNonAlphanumericCharacters = GetIntConfigValue (config, "minRequiredNonAlphanumericCharacters", 1);
1241			passwordAttemptWindow = GetIntConfigValue (config, "passwordAttemptWindow", 10);
1242			passwordStrengthRegularExpression = GetStringConfigValue (config, "passwordStrengthRegularExpression", "");
1243
1244			MembershipSection section = (MembershipSection)WebConfigurationManager.GetSection ("system.web/membership");
1245			
1246			userIsOnlineTimeWindow = section.UserIsOnlineTimeWindow;
1247
1248			/* we can't support password retrieval with hashed passwords */
1249			if (passwordFormat == MembershipPasswordFormat.Hashed && enablePasswordRetrieval)
1250				throw new ProviderException ("password retrieval cannot be used with hashed passwords");
1251
1252			string connectionStringName = config["connectionStringName"];
1253
1254			if (applicationName.Length > 256)
1255				throw new ProviderException ("The ApplicationName attribute must be 256 characters long or less.");
1256			if (connectionStringName == null || connectionStringName.Length == 0)
1257				throw new ProviderException ("The ConnectionStringName attribute must be present and non-zero length.");
1258
1259			connectionString = WebConfigurationManager.ConnectionStrings[connectionStringName];
1260		}
1261
1262		public override string ResetPassword (string username, string answer)
1263		{
1264			if (!enablePasswordReset)
1265				throw new NotSupportedException ("this provider has not been configured to allow the resetting of passwords");
1266
1267			CheckParam ("username", username, 256);
1268			if (RequiresQuestionAndAnswer)
1269				CheckParam ("answer", answer, 128);
1270
1271			MembershipUser user = GetUser (username, false);
1272			if (user == null) throw new ProviderException ("could not find user in membership database");
1273			if (user.IsLockedOut) throw new MembershipPasswordException ("user is currently locked out");
1274
1275			InitConnection();
1276
1277			string commandText;
1278			DbCommand command;
1279
1280			DbTransaction trans = connection.BeginTransaction ();
1281
1282			try {
1283				MembershipPasswordFormat db_passwordFormat;
1284				string db_salt;
1285				string newPassword = null;
1286
1287				if (ValidateUsingPasswordAnswer (trans, user.UserName, answer, out db_passwordFormat, out db_salt)) {
1288
1289					newPassword = GeneratePassword ();
1290					string db_password;
1291
1292					EmitValidatingPassword (username, newPassword, false);
1293
1294					/* otherwise update the user's password in the db */
1295
1296					db_password = EncodePassword (newPassword, db_passwordFormat, db_salt);
1297
1298					commandText = @"
1299UPDATE m
1300   SET Password = @Password,
1301       FailedPasswordAnswerAttemptCount = 0,
1302       FailedPasswordAnswerAttemptWindowStart = @DefaultDateTime
1303  FROM dbo.aspnet_Membership m, dbo.aspnet_Users u, dbo.aspnet_Applications a
1304 WHERE m.ApplicationId = a.ApplicationId
1305   AND u.ApplicationId = a.ApplicationId
1306   AND m.UserId = u.UserId
1307   AND u.LoweredUserName = LOWER(@UserName)
1308   AND a.LoweredApplicationName = LOWER(@ApplicationName)";
1309
1310					command = factory.CreateCommand ();
1311					command.Transaction = trans;
1312					command.CommandText = commandText;
1313					command.Connection = connection;
1314					command.CommandType = CommandType.Text;
1315					AddParameter (command, "UserName", user.UserName);
1316					AddParameter (command, "Password", db_password);
1317					AddParameter (command, "ApplicationName", ApplicationName);
1318					AddParameter (command, "DefaultDateTime", DefaultDateTime.ToString());
1319
1320					if (1 != (int)command.ExecuteNonQuery ())
1321						throw new ProviderException ("failed to update Membership table");
1322
1323					trans.Commit ();
1324				}
1325				else {
1326					throw new MembershipPasswordException ("The password-answer supplied is wrong.");
1327				}
1328
1329				return newPassword;
1330			}
1331			catch (MembershipPasswordException) {
1332				trans.Commit ();
1333				throw;
1334			}
1335			catch (ProviderException) {
1336				trans.Rollback ();
1337				throw;
1338			}
1339			catch (Exception e) {
1340				trans.Rollback ();
1341
1342				throw new ProviderException ("Failed to reset password", e);
1343			}
1344		}
1345		
1346		public override void UpdateUser (MembershipUser user)
1347		{
1348			if (user == null) throw new ArgumentNullException ("user");
1349			if (user.UserName == null) throw new ArgumentNullException ("user.UserName");
1350			if (RequiresUniqueEmail && user.Email == null) throw new ArgumentNullException ("user.Email");
1351
1352			CheckParam ("user.UserName", user.UserName, 256);
1353			if (user.Email.Length > 256 || (RequiresUniqueEmail && user.Email.Length == 0))
1354				throw new ArgumentException ("invalid format for user.Email");
1355
1356			DbTransaction trans = connection.BeginTransaction ();
1357
1358			string commandText;
1359			DbCommand command;
1360
1361			InitConnection();
1362
1363			try {
1364				DateTime now = DateTime.Now.ToUniversalTime ();
1365
1366				commandText = String.Format (@"
1367UPDATE m
1368   SET Email = {0},
1369       Comment = {1},
1370       IsApproved = @IsApproved,
1371       LastLoginDate = @Now
1372  FROM dbo.aspnet_Membership m, dbo.aspnet_Users u, dbo.aspnet_Applications a
1373 WHERE m.ApplicationId = a.ApplicationId
1374   AND u.ApplicationId = a.ApplicationId
1375   AND m.UserId = u.UserId
1376   AND u.LoweredUserName = LOWER(@UserName)
1377   AND a.LoweredApplicationName = LOWER(@ApplicationName)",
1378							     user.Email == null ? "NULL" : "@Email",
1379							     user.Comment == null ? "NULL" : "@Comment");
1380
1381				command = factory.CreateCommand ();
1382				command.Transaction = trans;
1383				command.CommandText = commandText;
1384				command.Connection = connection;
1385				command.CommandType = CommandType.Text;
1386				if (user.Email != null)
1387					AddParameter (command, "Email", user.Email);
1388				if (user.Comment != null)
1389					AddParameter (command, "Comment", user.Comment);
1390				AddParameter (command, "IsApproved", user.IsApproved.ToString());
1391				AddParameter (command, "UserName", user.UserName);
1392				AddParameter (command, "ApplicationName", ApplicationName);
1393				AddParameter (command, "Now", now.ToString ());
1394
1395				if (0 == command.ExecuteNonQuery())
1396					throw new ProviderException ("failed to membership table");
1397
1398
1399				commandText = @"
1400UPDATE dbo.aspnet_Users
1401   SET LastActivityDate = @Now
1402  FROM dbo.aspnet_Users u, dbo.aspnet_Applications a
1403 WHERE a.ApplicationId = a.ApplicationId
1404   AND u.LoweredUserName = LOWER(@UserName)
1405   AND a.LoweredApplicationName = LOWER(@ApplicationName)";
1406
1407				command = factory.CreateCommand ();
1408				command.Transaction = trans;
1409				command.CommandText = commandText;
1410				command.Connection = connection;
1411				command.CommandType = CommandType.Text;
1412				AddParameter (command, "UserName", user.UserName);
1413				AddParameter (command, "ApplicationName", ApplicationName);
1414				AddParameter (command, "Now", now.ToString ());
1415
1416				if (0 == command.ExecuteNonQuery())
1417					throw new ProviderException ("failed to user table");
1418
1419				trans.Commit ();
1420			}
1421			catch (ProviderException) {
1422				trans.Rollback ();
1423				throw;
1424			}
1425			catch (Exception e) {
1426				trans.Rollback ();
1427				throw new ProviderException ("failed to update user", e);
1428			}
1429		}
1430		
1431		public override bool ValidateUser (string username, string password)
1432		{
1433			MembershipUser user = GetUser (username, false);
1434
1435			/* if the user is locked out, return false immediately */
1436			if (user.IsLockedOut)
1437				return false;
1438
1439			/* if the user is not yet approved, return false */
1440			if (!user.IsApproved)
1441				return false;
1442
1443			EmitValidatingPassword (username, password, false);
1444
1445			InitConnection();
1446
1447			DbTransaction trans = connection.BeginTransaction ();
1448
1449			string commandText;
1450			DbCommand command;
1451
1452			try {
1453				MembershipPasswordFormat passwordFormat;
1454				string salt;
1455
1456				bool valid = ValidateUsingPassword (trans, username, password, out passwordFormat, out salt);
1457				if (valid) {
1458
1459					DateTime now = DateTime.Now.ToUniversalTime ();
1460
1461					/* if the validation succeeds:
1462					   set LastLoginDate to DateTime.Now
1463					   set FailedPasswordAttemptCount to 0
1464					   set FailedPasswordAttemptWindow to DefaultDateTime
1465					   set FailedPasswordAnswerAttemptCount to 0
1466					   set FailedPasswordAnswerAttemptWindowStart to DefaultDateTime
1467					*/
1468
1469					commandText = @"
1470UPDATE dbo.aspnet_Membership
1471   SET LastLoginDate = @Now,
1472       FailedPasswordAttemptCount = 0,
1473       FailedPasswordAttemptWindowStart = @DefaultDateTime,
1474       FailedPasswordAnswerAttemptCount = 0,
1475       FailedPasswordAnswerAttemptWindowStart = @DefaultDateTime
1476  FROM dbo.aspnet_Membership m, dbo.aspnet_Applications a, dbo.aspnet_Users u
1477 WHERE m.ApplicationId = a.ApplicationId
1478   AND u.ApplicationId = a.ApplicationId
1479   AND m.UserId = u.UserId
1480   AND u.LoweredUserName = LOWER(@UserName)
1481   AND a.LoweredApplicationName = LOWER(@ApplicationName)";
1482
1483					command = factory.CreateCommand ();
1484					command.Transaction = trans;
1485					command.CommandText = commandText;
1486					command.Connection = connection;
1487					command.CommandType = CommandType.Text;
1488					AddParameter (command, "UserName", user.UserName);
1489					AddParameter (command, "ApplicationName", ApplicationName);
1490					AddParameter (command, "Now", now.ToString ());
1491					AddParameter (command, "DefaultDateTime",

Large files files are truncated, but you can click here to view the full file