PageRenderTime 65ms CodeModel.GetById 27ms RepoModel.GetById 1ms app.codeStats 0ms

/mcs/class/System.Web/System.Web.Security/SqlMembershipProvider.cs

https://github.com/ztfuqingvip/mono
C# | 1241 lines | 984 code | 210 blank | 47 comment | 215 complexity | 59edefce4a82514a9b59d7294793f9ec MD5 | raw file
Possible License(s): GPL-2.0, Unlicense, MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0
  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. #if NET_2_0
  32. using System.Collections;
  33. using System.Collections.Specialized;
  34. using System.Configuration;
  35. using System.Configuration.Provider;
  36. using System.Data;
  37. using System.Data.Common;
  38. using System.Text;
  39. using System.Web.Configuration;
  40. using System.Security.Cryptography;
  41. namespace System.Web.Security {
  42. public class SqlMembershipProvider : MembershipProvider
  43. {
  44. bool enablePasswordReset;
  45. bool enablePasswordRetrieval;
  46. int maxInvalidPasswordAttempts;
  47. MembershipPasswordFormat passwordFormat;
  48. bool requiresQuestionAndAnswer;
  49. bool requiresUniqueEmail;
  50. int minRequiredNonAlphanumericCharacters;
  51. int minRequiredPasswordLength;
  52. int passwordAttemptWindow;
  53. string passwordStrengthRegularExpression;
  54. TimeSpan userIsOnlineTimeWindow;
  55. ConnectionStringSettings connectionString;
  56. DbProviderFactory factory;
  57. string applicationName;
  58. bool schemaIsOk = false;
  59. DbConnection CreateConnection ()
  60. {
  61. if (!schemaIsOk && !(schemaIsOk = AspNetDBSchemaChecker.CheckMembershipSchemaVersion (factory, connectionString.ConnectionString, "membership", "1")))
  62. throw new ProviderException ("Incorrect ASP.NET DB Schema Version.");
  63. DbConnection connection;
  64. if (connectionString == null)
  65. throw new ProviderException ("Connection string for the SQL Membership Provider has not been provided.");
  66. try {
  67. connection = factory.CreateConnection ();
  68. connection.ConnectionString = connectionString.ConnectionString;
  69. connection.Open ();
  70. } catch (Exception ex) {
  71. throw new ProviderException ("Unable to open SQL connection for the SQL Membership Provider.",
  72. ex);
  73. }
  74. return connection;
  75. }
  76. DbParameter AddParameter (DbCommand command, string parameterName, object parameterValue)
  77. {
  78. return AddParameter (command, parameterName, ParameterDirection.Input, parameterValue);
  79. }
  80. DbParameter AddParameter (DbCommand command, string parameterName, ParameterDirection direction, object parameterValue)
  81. {
  82. DbParameter dbp = command.CreateParameter ();
  83. dbp.ParameterName = parameterName;
  84. dbp.Value = parameterValue;
  85. dbp.Direction = direction;
  86. command.Parameters.Add (dbp);
  87. return dbp;
  88. }
  89. DbParameter AddParameter (DbCommand command, string parameterName, ParameterDirection direction, DbType type, object parameterValue)
  90. {
  91. DbParameter dbp = command.CreateParameter ();
  92. dbp.ParameterName = parameterName;
  93. dbp.Value = parameterValue;
  94. dbp.Direction = direction;
  95. dbp.DbType = type;
  96. command.Parameters.Add (dbp);
  97. return dbp;
  98. }
  99. static int GetReturnValue (DbParameter returnValue)
  100. {
  101. object value = returnValue.Value;
  102. return value is int ? (int) value : -1;
  103. }
  104. void CheckParam (string pName, string p, int length)
  105. {
  106. if (p == null)
  107. throw new ArgumentNullException (pName);
  108. if (p.Length == 0 || p.Length > length || p.IndexOf (',') != -1)
  109. throw new ArgumentException (String.Format ("invalid format for {0}", pName));
  110. }
  111. public override bool ChangePassword (string username, string oldPwd, string newPwd)
  112. {
  113. if (username != null) username = username.Trim ();
  114. if (oldPwd != null) oldPwd = oldPwd.Trim ();
  115. if (newPwd != null) newPwd = newPwd.Trim ();
  116. CheckParam ("username", username, 256);
  117. CheckParam ("oldPwd", oldPwd, 128);
  118. CheckParam ("newPwd", newPwd, 128);
  119. if (!CheckPassword (newPwd))
  120. throw new ArgumentException (string.Format (
  121. "New Password invalid. New Password length minimum: {0}. Non-alphanumeric characters required: {1}.",
  122. MinRequiredPasswordLength,
  123. MinRequiredNonAlphanumericCharacters));
  124. using (DbConnection connection = CreateConnection ()) {
  125. PasswordInfo pi = ValidateUsingPassword (username, oldPwd);
  126. if (pi != null) {
  127. EmitValidatingPassword (username, newPwd, false);
  128. string db_password = EncodePassword (newPwd, pi.PasswordFormat, pi.PasswordSalt);
  129. DbCommand command = factory.CreateCommand ();
  130. command.Connection = connection;
  131. command.CommandText = @"aspnet_Membership_SetPassword";
  132. command.CommandType = CommandType.StoredProcedure;
  133. AddParameter (command, "@ApplicationName", ApplicationName);
  134. AddParameter (command, "@UserName", username);
  135. AddParameter (command, "@NewPassword", db_password);
  136. AddParameter (command, "@PasswordFormat", (int) pi.PasswordFormat);
  137. AddParameter (command, "@PasswordSalt", pi.PasswordSalt);
  138. AddParameter (command, "@CurrentTimeUtc", DateTime.UtcNow);
  139. DbParameter returnValue = AddParameter (command, "@ReturnVal", ParameterDirection.ReturnValue, DbType.Int32, null);
  140. command.ExecuteNonQuery ();
  141. if (GetReturnValue (returnValue) != 0)
  142. return false;
  143. return true;
  144. }
  145. return false;
  146. }
  147. }
  148. public override bool ChangePasswordQuestionAndAnswer (string username, string password, string newPwdQuestion, string newPwdAnswer)
  149. {
  150. if (username != null) username = username.Trim ();
  151. if (newPwdQuestion != null) newPwdQuestion = newPwdQuestion.Trim ();
  152. if (newPwdAnswer != null) newPwdAnswer = newPwdAnswer.Trim ();
  153. CheckParam ("username", username, 256);
  154. if (RequiresQuestionAndAnswer)
  155. CheckParam ("newPwdQuestion", newPwdQuestion, 128);
  156. if (RequiresQuestionAndAnswer)
  157. CheckParam ("newPwdAnswer", newPwdAnswer, 128);
  158. using (DbConnection connection = CreateConnection ()) {
  159. PasswordInfo pi = ValidateUsingPassword (username, password);
  160. if (pi != null) {
  161. string db_passwordAnswer = EncodePassword (newPwdAnswer, pi.PasswordFormat, pi.PasswordSalt);
  162. DbCommand command = factory.CreateCommand ();
  163. command.Connection = connection;
  164. command.CommandType = CommandType.StoredProcedure;
  165. command.CommandText = @"aspnet_Membership_ChangePasswordQuestionAndAnswer";
  166. AddParameter (command, "@ApplicationName", ApplicationName);
  167. AddParameter (command, "@UserName", username);
  168. AddParameter (command, "@NewPasswordQuestion", newPwdQuestion);
  169. AddParameter (command, "@NewPasswordAnswer", db_passwordAnswer);
  170. DbParameter returnValue = AddParameter (command, "@ReturnVal", ParameterDirection.ReturnValue, DbType.Int32, null);
  171. command.ExecuteNonQuery ();
  172. if (GetReturnValue (returnValue) != 0)
  173. return false;
  174. return true;
  175. }
  176. return false;
  177. }
  178. }
  179. public override MembershipUser CreateUser (string username,
  180. string password,
  181. string email,
  182. string pwdQuestion,
  183. string pwdAnswer,
  184. bool isApproved,
  185. object providerUserKey,
  186. out MembershipCreateStatus status)
  187. {
  188. if (username != null) username = username.Trim ();
  189. if (password != null) password = password.Trim ();
  190. if (email != null) email = email.Trim ();
  191. if (pwdQuestion != null) pwdQuestion = pwdQuestion.Trim ();
  192. if (pwdAnswer != null) pwdAnswer = pwdAnswer.Trim ();
  193. /* some initial validation */
  194. if (username == null || username.Length == 0 || username.Length > 256 || username.IndexOf (',') != -1) {
  195. status = MembershipCreateStatus.InvalidUserName;
  196. return null;
  197. }
  198. if (password == null || password.Length == 0 || password.Length > 128) {
  199. status = MembershipCreateStatus.InvalidPassword;
  200. return null;
  201. }
  202. if (!CheckPassword (password)) {
  203. status = MembershipCreateStatus.InvalidPassword;
  204. return null;
  205. }
  206. EmitValidatingPassword (username, password, true);
  207. if (RequiresUniqueEmail && (email == null || email.Length == 0)) {
  208. status = MembershipCreateStatus.InvalidEmail;
  209. return null;
  210. }
  211. if (RequiresQuestionAndAnswer &&
  212. (pwdQuestion == null ||
  213. pwdQuestion.Length == 0 || pwdQuestion.Length > 256)) {
  214. status = MembershipCreateStatus.InvalidQuestion;
  215. return null;
  216. }
  217. if (RequiresQuestionAndAnswer &&
  218. (pwdAnswer == null ||
  219. pwdAnswer.Length == 0 || pwdAnswer.Length > 128)) {
  220. status = MembershipCreateStatus.InvalidAnswer;
  221. return null;
  222. }
  223. if (providerUserKey != null && !(providerUserKey is Guid)) {
  224. status = MembershipCreateStatus.InvalidProviderUserKey;
  225. return null;
  226. }
  227. if (providerUserKey == null)
  228. providerUserKey = Guid.NewGuid();
  229. /* encode our password/answer using the
  230. * "passwordFormat" configuration option */
  231. string passwordSalt = "";
  232. RandomNumberGenerator rng = RandomNumberGenerator.Create ();
  233. byte [] salt = new byte [MembershipHelper.SALT_BYTES];
  234. rng.GetBytes (salt);
  235. passwordSalt = Convert.ToBase64String (salt);
  236. password = EncodePassword (password, PasswordFormat, passwordSalt);
  237. if (RequiresQuestionAndAnswer)
  238. pwdAnswer = EncodePassword (pwdAnswer, PasswordFormat, passwordSalt);
  239. /* make sure the hashed/encrypted password and
  240. * answer are still under 128 characters. */
  241. if (password.Length > 128) {
  242. status = MembershipCreateStatus.InvalidPassword;
  243. return null;
  244. }
  245. if (RequiresQuestionAndAnswer) {
  246. if (pwdAnswer.Length > 128) {
  247. status = MembershipCreateStatus.InvalidAnswer;
  248. return null;
  249. }
  250. }
  251. status = MembershipCreateStatus.Success;
  252. using (DbConnection connection = CreateConnection ()) {
  253. try {
  254. DbCommand command = factory.CreateCommand ();
  255. command.Connection = connection;
  256. command.CommandText = @"aspnet_Membership_CreateUser";
  257. command.CommandType = CommandType.StoredProcedure;
  258. DateTime Now = DateTime.UtcNow;
  259. AddParameter (command, "@ApplicationName", ApplicationName);
  260. AddParameter (command, "@UserName", username);
  261. AddParameter (command, "@Password", password);
  262. AddParameter (command, "@PasswordSalt", passwordSalt);
  263. AddParameter (command, "@Email", email);
  264. AddParameter (command, "@PasswordQuestion", pwdQuestion);
  265. AddParameter (command, "@PasswordAnswer", pwdAnswer);
  266. AddParameter (command, "@IsApproved", isApproved);
  267. AddParameter (command, "@CurrentTimeUtc", Now);
  268. AddParameter (command, "@CreateDate", Now);
  269. AddParameter (command, "@UniqueEmail", RequiresUniqueEmail);
  270. AddParameter (command, "@PasswordFormat", (int) PasswordFormat);
  271. AddParameter (command, "@UserId", ParameterDirection.InputOutput, providerUserKey);
  272. DbParameter returnValue = AddParameter (command, "@ReturnVal", ParameterDirection.ReturnValue, DbType.Int32, null);
  273. command.ExecuteNonQuery ();
  274. int st = GetReturnValue (returnValue);
  275. if (st == 0)
  276. return GetUser (username, false);
  277. else if (st == 6)
  278. status = MembershipCreateStatus.DuplicateUserName;
  279. else if (st == 7)
  280. status = MembershipCreateStatus.DuplicateEmail;
  281. else if (st == 10)
  282. status = MembershipCreateStatus.DuplicateProviderUserKey;
  283. else
  284. status = MembershipCreateStatus.ProviderError;
  285. return null;
  286. }
  287. catch (Exception) {
  288. status = MembershipCreateStatus.ProviderError;
  289. return null;
  290. }
  291. }
  292. }
  293. bool CheckPassword (string password)
  294. {
  295. if (password.Length < MinRequiredPasswordLength)
  296. return false;
  297. if (MinRequiredNonAlphanumericCharacters > 0) {
  298. int nonAlphanumeric = 0;
  299. for (int i = 0; i < password.Length; i++) {
  300. if (!Char.IsLetterOrDigit (password [i]))
  301. nonAlphanumeric++;
  302. }
  303. return nonAlphanumeric >= MinRequiredNonAlphanumericCharacters;
  304. }
  305. return true;
  306. }
  307. public override bool DeleteUser (string username, bool deleteAllRelatedData)
  308. {
  309. CheckParam ("username", username, 256);
  310. DeleteUserTableMask deleteBitmask = DeleteUserTableMask.MembershipUsers;
  311. if (deleteAllRelatedData)
  312. deleteBitmask |=
  313. DeleteUserTableMask.Profiles |
  314. DeleteUserTableMask.UsersInRoles |
  315. DeleteUserTableMask.WebPartStateUser;
  316. using (DbConnection connection = CreateConnection ()) {
  317. DbCommand command = factory.CreateCommand ();
  318. command.Connection = connection;
  319. command.CommandText = @"aspnet_Users_DeleteUser";
  320. command.CommandType = CommandType.StoredProcedure;
  321. AddParameter (command, "@ApplicationName", ApplicationName);
  322. AddParameter (command, "@UserName", username);
  323. AddParameter (command, "@TablesToDeleteFrom", (int) deleteBitmask);
  324. AddParameter (command, "@NumTablesDeletedFrom", ParameterDirection.Output, 0);
  325. DbParameter returnValue = AddParameter (command, "@ReturnVal", ParameterDirection.ReturnValue, DbType.Int32, null);
  326. command.ExecuteNonQuery ();
  327. if (((int) command.Parameters ["@NumTablesDeletedFrom"].Value) == 0)
  328. return false;
  329. if (GetReturnValue (returnValue) == 0)
  330. return true;
  331. return false;
  332. }
  333. }
  334. public virtual string GeneratePassword ()
  335. {
  336. return Membership.GeneratePassword (MinRequiredPasswordLength, MinRequiredNonAlphanumericCharacters);
  337. }
  338. public override MembershipUserCollection FindUsersByEmail (string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
  339. {
  340. CheckParam ("emailToMatch", emailToMatch, 256);
  341. if (pageIndex < 0)
  342. throw new ArgumentException ("pageIndex must be >= 0");
  343. if (pageSize < 0)
  344. throw new ArgumentException ("pageSize must be >= 0");
  345. if (pageIndex * pageSize + pageSize - 1 > Int32.MaxValue)
  346. throw new ArgumentException ("pageIndex and pageSize are too large");
  347. using (DbConnection connection = CreateConnection ()) {
  348. DbCommand command = factory.CreateCommand ();
  349. command.Connection = connection;
  350. command.CommandText = @"aspnet_Membership_FindUsersByEmail";
  351. command.CommandType = CommandType.StoredProcedure;
  352. AddParameter (command, "@PageIndex", pageIndex);
  353. AddParameter (command, "@PageSize", pageSize);
  354. AddParameter (command, "@EmailToMatch", emailToMatch);
  355. AddParameter (command, "@ApplicationName", ApplicationName);
  356. // return value
  357. AddParameter (command, "@ReturnValue", ParameterDirection.ReturnValue, null);
  358. MembershipUserCollection c = BuildMembershipUserCollection (command, pageIndex, pageSize, out totalRecords);
  359. return c;
  360. }
  361. }
  362. public override MembershipUserCollection FindUsersByName (string nameToMatch, int pageIndex, int pageSize, out int totalRecords)
  363. {
  364. CheckParam ("nameToMatch", nameToMatch, 256);
  365. if (pageIndex < 0)
  366. throw new ArgumentException ("pageIndex must be >= 0");
  367. if (pageSize < 0)
  368. throw new ArgumentException ("pageSize must be >= 0");
  369. if (pageIndex * pageSize + pageSize - 1 > Int32.MaxValue)
  370. throw new ArgumentException ("pageIndex and pageSize are too large");
  371. using (DbConnection connection = CreateConnection ()) {
  372. DbCommand command = factory.CreateCommand ();
  373. command.Connection = connection;
  374. command.CommandText = @"aspnet_Membership_FindUsersByName";
  375. command.CommandType = CommandType.StoredProcedure;
  376. AddParameter (command, "@PageIndex", pageIndex);
  377. AddParameter (command, "@PageSize", pageSize);
  378. AddParameter (command, "@UserNameToMatch", nameToMatch);
  379. AddParameter (command, "@ApplicationName", ApplicationName);
  380. // return value
  381. AddParameter (command, "@ReturnValue", ParameterDirection.ReturnValue, null);
  382. MembershipUserCollection c = BuildMembershipUserCollection (command, pageIndex, pageSize, out totalRecords);
  383. return c;
  384. }
  385. }
  386. public override MembershipUserCollection GetAllUsers (int pageIndex, int pageSize, out int totalRecords)
  387. {
  388. if (pageIndex < 0)
  389. throw new ArgumentException ("pageIndex must be >= 0");
  390. if (pageSize < 0)
  391. throw new ArgumentException ("pageSize must be >= 0");
  392. if (pageIndex * pageSize + pageSize - 1 > Int32.MaxValue)
  393. throw new ArgumentException ("pageIndex and pageSize are too large");
  394. using (DbConnection connection = CreateConnection ()) {
  395. DbCommand command = factory.CreateCommand ();
  396. command.Connection = connection;
  397. command.CommandText = @"aspnet_Membership_GetAllUsers";
  398. command.CommandType = CommandType.StoredProcedure;
  399. AddParameter (command, "@ApplicationName", ApplicationName);
  400. AddParameter (command, "@PageIndex", pageIndex);
  401. AddParameter (command, "@PageSize", pageSize);
  402. // return value
  403. AddParameter (command, "@ReturnValue", ParameterDirection.ReturnValue, null);
  404. MembershipUserCollection c = BuildMembershipUserCollection (command, pageIndex, pageSize, out totalRecords);
  405. return c;
  406. }
  407. }
  408. MembershipUserCollection BuildMembershipUserCollection (DbCommand command, int pageIndex, int pageSize, out int totalRecords)
  409. {
  410. DbDataReader reader = null;
  411. try {
  412. MembershipUserCollection users = new MembershipUserCollection ();
  413. reader = command.ExecuteReader ();
  414. while (reader.Read ())
  415. users.Add (GetUserFromReader (reader, null, null));
  416. totalRecords = Convert.ToInt32 (command.Parameters ["@ReturnValue"].Value);
  417. return users;
  418. } catch (Exception) {
  419. totalRecords = 0;
  420. return null; /* should we let the exception through? */
  421. }
  422. finally {
  423. if (reader != null)
  424. reader.Close ();
  425. }
  426. }
  427. public override int GetNumberOfUsersOnline ()
  428. {
  429. using (DbConnection connection = CreateConnection ()) {
  430. DateTime now = DateTime.UtcNow;
  431. DbCommand command = factory.CreateCommand ();
  432. command.Connection = connection;
  433. command.CommandText = @"aspnet_Membership_GetNumberOfUsersOnline";
  434. command.CommandType = CommandType.StoredProcedure;
  435. AddParameter (command, "@CurrentTimeUtc", now.ToString ());
  436. AddParameter (command, "@ApplicationName", ApplicationName);
  437. AddParameter (command, "@MinutesSinceLastInActive", userIsOnlineTimeWindow.Minutes);
  438. DbParameter returnValue = AddParameter (command, "@ReturnVal", ParameterDirection.ReturnValue, DbType.Int32, null);
  439. command.ExecuteScalar ();
  440. return GetReturnValue (returnValue);
  441. }
  442. }
  443. public override string GetPassword (string username, string answer)
  444. {
  445. if (!EnablePasswordRetrieval)
  446. throw new NotSupportedException ("this provider has not been configured to allow the retrieval of passwords");
  447. CheckParam ("username", username, 256);
  448. if (RequiresQuestionAndAnswer)
  449. CheckParam ("answer", answer, 128);
  450. PasswordInfo pi = GetPasswordInfo (username);
  451. if (pi == null)
  452. throw new ProviderException ("An error occurred while retrieving the password from the database");
  453. string user_answer = EncodePassword (answer, pi.PasswordFormat, pi.PasswordSalt);
  454. string password = null;
  455. using (DbConnection connection = CreateConnection ()) {
  456. DbCommand command = factory.CreateCommand ();
  457. command.Connection = connection;
  458. command.CommandText = @"aspnet_Membership_GetPassword";
  459. command.CommandType = CommandType.StoredProcedure;
  460. AddParameter (command, "@ApplicationName", ApplicationName);
  461. AddParameter (command, "@UserName", username);
  462. AddParameter (command, "@MaxInvalidPasswordAttempts", MaxInvalidPasswordAttempts);
  463. AddParameter (command, "@PasswordAttemptWindow", PasswordAttemptWindow);
  464. AddParameter (command, "@CurrentTimeUtc", DateTime.UtcNow);
  465. AddParameter (command, "@PasswordAnswer", user_answer);
  466. DbParameter retValue = AddParameter (command, "@ReturnVal", ParameterDirection.ReturnValue, DbType.Int32, null);
  467. DbDataReader reader = command.ExecuteReader ();
  468. int returnValue = GetReturnValue (retValue);
  469. if (returnValue == 3)
  470. throw new MembershipPasswordException ("Password Answer is invalid");
  471. if (returnValue == 99)
  472. throw new MembershipPasswordException ("The user account is currently locked out");
  473. if (reader.Read ()) {
  474. password = reader.GetString (0);
  475. reader.Close ();
  476. }
  477. if (pi.PasswordFormat == MembershipPasswordFormat.Clear)
  478. return password;
  479. else if (pi.PasswordFormat == MembershipPasswordFormat.Encrypted)
  480. return DecodePassword (password, pi.PasswordFormat);
  481. return password;
  482. }
  483. }
  484. MembershipUser GetUserFromReader (DbDataReader reader, string username, object userId)
  485. {
  486. int i = 0;
  487. if (username == null)
  488. i = 1;
  489. if (userId != null)
  490. username = reader.GetString (8);
  491. return new MembershipUser (this.Name, /* XXX is this right? */
  492. (username == null ? reader.GetString (0) : username), /* name */
  493. (userId == null ? reader.GetGuid (8 + i) : userId), /* providerUserKey */
  494. reader.IsDBNull (0 + i) ? null : reader.GetString (0 + i), /* email */
  495. reader.IsDBNull (1 + i) ? null : reader.GetString (1 + i), /* passwordQuestion */
  496. reader.IsDBNull (2 + i) ? null : reader.GetString (2 + i), /* comment */
  497. reader.GetBoolean (3 + i), /* isApproved */
  498. reader.GetBoolean (9 + i), /* isLockedOut */
  499. reader.GetDateTime (4 + i).ToLocalTime (), /* creationDate */
  500. reader.GetDateTime (5 + i).ToLocalTime (), /* lastLoginDate */
  501. reader.GetDateTime (6 + i).ToLocalTime (), /* lastActivityDate */
  502. reader.GetDateTime (7 + i).ToLocalTime (), /* lastPasswordChangedDate */
  503. reader.GetDateTime (10 + i).ToLocalTime () /* lastLockoutDate */);
  504. }
  505. MembershipUser BuildMembershipUser (DbCommand query, string username, object userId)
  506. {
  507. try {
  508. using (DbConnection connection = CreateConnection ()) {
  509. query.Connection = connection;
  510. using (DbDataReader reader = query.ExecuteReader ()) {
  511. if (!reader.Read ())
  512. return null;
  513. return GetUserFromReader (reader, username, userId);
  514. }
  515. }
  516. } catch (Exception) {
  517. return null; /* should we let the exception through? */
  518. }
  519. finally {
  520. query.Connection = null;
  521. }
  522. }
  523. public override MembershipUser GetUser (string username, bool userIsOnline)
  524. {
  525. if (username == null)
  526. throw new ArgumentNullException ("username");
  527. if (username.Length == 0)
  528. return null;
  529. CheckParam ("username", username, 256);
  530. DbCommand command = factory.CreateCommand ();
  531. command.CommandText = @"aspnet_Membership_GetUserByName";
  532. command.CommandType = CommandType.StoredProcedure;
  533. AddParameter (command, "@UserName", username);
  534. AddParameter (command, "@ApplicationName", ApplicationName);
  535. AddParameter (command, "@CurrentTimeUtc", DateTime.Now);
  536. AddParameter (command, "@UpdateLastActivity", userIsOnline);
  537. MembershipUser u = BuildMembershipUser (command, username, null);
  538. return u;
  539. }
  540. public override MembershipUser GetUser (object providerUserKey, bool userIsOnline)
  541. {
  542. DbCommand command = factory.CreateCommand ();
  543. command.CommandText = @"aspnet_Membership_GetUserByUserId";
  544. command.CommandType = CommandType.StoredProcedure;
  545. AddParameter (command, "@UserId", providerUserKey);
  546. AddParameter (command, "@CurrentTimeUtc", DateTime.Now);
  547. AddParameter (command, "@UpdateLastActivity", userIsOnline);
  548. MembershipUser u = BuildMembershipUser (command, string.Empty, providerUserKey);
  549. return u;
  550. }
  551. public override string GetUserNameByEmail (string email)
  552. {
  553. CheckParam ("email", email, 256);
  554. using (DbConnection connection = CreateConnection ()) {
  555. DbCommand command = factory.CreateCommand ();
  556. command.Connection = connection;
  557. command.CommandText = @"aspnet_Membership_GetUserByEmail";
  558. command.CommandType = CommandType.StoredProcedure;
  559. AddParameter (command, "@ApplicationName", ApplicationName);
  560. AddParameter (command, "@Email", email);
  561. DbDataReader reader = command.ExecuteReader ();
  562. string rv = null;
  563. if (reader.Read ())
  564. rv = reader.GetString (0);
  565. reader.Close ();
  566. return rv;
  567. }
  568. }
  569. bool GetBoolConfigValue (NameValueCollection config, string name, bool def)
  570. {
  571. bool rv = def;
  572. string val = config [name];
  573. if (val != null) {
  574. try { rv = Boolean.Parse (val); }
  575. catch (Exception e) {
  576. throw new ProviderException (String.Format ("{0} must be true or false", name), e);
  577. }
  578. }
  579. return rv;
  580. }
  581. int GetIntConfigValue (NameValueCollection config, string name, int def)
  582. {
  583. int rv = def;
  584. string val = config [name];
  585. if (val != null) {
  586. try { rv = Int32.Parse (val); }
  587. catch (Exception e) {
  588. throw new ProviderException (String.Format ("{0} must be an integer", name), e);
  589. }
  590. }
  591. return rv;
  592. }
  593. int GetEnumConfigValue (NameValueCollection config, string name, Type enumType, int def)
  594. {
  595. int rv = def;
  596. string val = config [name];
  597. if (val != null) {
  598. try { rv = (int) Enum.Parse (enumType, val); }
  599. catch (Exception e) {
  600. throw new ProviderException (String.Format ("{0} must be one of the following values: {1}", name, String.Join (",", Enum.GetNames (enumType))), e);
  601. }
  602. }
  603. return rv;
  604. }
  605. string GetStringConfigValue (NameValueCollection config, string name, string def)
  606. {
  607. string rv = def;
  608. string val = config [name];
  609. if (val != null)
  610. rv = val;
  611. return rv;
  612. }
  613. void EmitValidatingPassword (string username, string password, bool isNewUser)
  614. {
  615. ValidatePasswordEventArgs args = new ValidatePasswordEventArgs (username, password, isNewUser);
  616. OnValidatingPassword (args);
  617. /* if we're canceled.. */
  618. if (args.Cancel) {
  619. if (args.FailureInformation == null)
  620. throw new ProviderException ("Password validation canceled");
  621. else
  622. throw args.FailureInformation;
  623. }
  624. }
  625. public override void Initialize (string name, NameValueCollection config)
  626. {
  627. if (config == null)
  628. throw new ArgumentNullException ("config");
  629. base.Initialize (name, config);
  630. applicationName = GetStringConfigValue (config, "applicationName", "/");
  631. enablePasswordReset = GetBoolConfigValue (config, "enablePasswordReset", true);
  632. enablePasswordRetrieval = GetBoolConfigValue (config, "enablePasswordRetrieval", false);
  633. requiresQuestionAndAnswer = GetBoolConfigValue (config, "requiresQuestionAndAnswer", true);
  634. requiresUniqueEmail = GetBoolConfigValue (config, "requiresUniqueEmail", false);
  635. passwordFormat = (MembershipPasswordFormat) GetEnumConfigValue (config, "passwordFormat", typeof (MembershipPasswordFormat),
  636. (int) MembershipPasswordFormat.Hashed);
  637. maxInvalidPasswordAttempts = GetIntConfigValue (config, "maxInvalidPasswordAttempts", 5);
  638. minRequiredPasswordLength = GetIntConfigValue (config, "minRequiredPasswordLength", 7);
  639. minRequiredNonAlphanumericCharacters = GetIntConfigValue (config, "minRequiredNonalphanumericCharacters", 1);
  640. passwordAttemptWindow = GetIntConfigValue (config, "passwordAttemptWindow", 10);
  641. passwordStrengthRegularExpression = GetStringConfigValue (config, "passwordStrengthRegularExpression", "");
  642. MembershipSection section = (MembershipSection) WebConfigurationManager.GetSection ("system.web/membership");
  643. userIsOnlineTimeWindow = section.UserIsOnlineTimeWindow;
  644. /* we can't support password retrieval with hashed passwords */
  645. if (passwordFormat == MembershipPasswordFormat.Hashed && enablePasswordRetrieval)
  646. throw new ProviderException ("password retrieval cannot be used with hashed passwords");
  647. string connectionStringName = config ["connectionStringName"];
  648. if (applicationName.Length > 256)
  649. throw new ProviderException ("The ApplicationName attribute must be 256 characters long or less.");
  650. if (connectionStringName == null || connectionStringName.Length == 0)
  651. throw new ProviderException ("The ConnectionStringName attribute must be present and non-zero length.");
  652. connectionString = WebConfigurationManager.ConnectionStrings [connectionStringName];
  653. factory = connectionString == null || String.IsNullOrEmpty (connectionString.ProviderName) ?
  654. System.Data.SqlClient.SqlClientFactory.Instance :
  655. ProvidersHelper.GetDbProviderFactory (connectionString.ProviderName);
  656. }
  657. public override string ResetPassword (string username, string answer)
  658. {
  659. if (!EnablePasswordReset)
  660. throw new NotSupportedException ("this provider has not been configured to allow the resetting of passwords");
  661. CheckParam ("username", username, 256);
  662. if (RequiresQuestionAndAnswer)
  663. CheckParam ("answer", answer, 128);
  664. using (DbConnection connection = CreateConnection ()) {
  665. PasswordInfo pi = GetPasswordInfo (username);
  666. if (pi == null)
  667. throw new ProviderException (username + "is not found in the membership database");
  668. string newPassword = GeneratePassword ();
  669. EmitValidatingPassword (username, newPassword, false);
  670. string db_password = EncodePassword (newPassword, pi.PasswordFormat, pi.PasswordSalt);
  671. string db_answer = EncodePassword (answer, pi.PasswordFormat, pi.PasswordSalt);
  672. DbCommand command = factory.CreateCommand ();
  673. command.Connection = connection;
  674. command.CommandText = @"aspnet_Membership_ResetPassword";
  675. command.CommandType = CommandType.StoredProcedure;
  676. AddParameter (command, "@ApplicationName", ApplicationName);
  677. AddParameter (command, "@UserName", username);
  678. AddParameter (command, "@NewPassword", db_password);
  679. AddParameter (command, "@MaxInvalidPasswordAttempts", MaxInvalidPasswordAttempts);
  680. AddParameter (command, "@PasswordAttemptWindow", PasswordAttemptWindow);
  681. AddParameter (command, "@PasswordSalt", pi.PasswordSalt);
  682. AddParameter (command, "@CurrentTimeUtc", DateTime.UtcNow);
  683. AddParameter (command, "@PasswordFormat", (int) pi.PasswordFormat);
  684. AddParameter (command, "@PasswordAnswer", db_answer);
  685. DbParameter retValue = AddParameter (command, "@ReturnVal", ParameterDirection.ReturnValue, DbType.Int32, null);
  686. command.ExecuteNonQuery ();
  687. int returnValue = GetReturnValue (retValue);
  688. if (returnValue == 0)
  689. return newPassword;
  690. else if (returnValue == 3)
  691. throw new MembershipPasswordException ("Password Answer is invalid");
  692. else if (returnValue == 99)
  693. throw new MembershipPasswordException ("The user account is currently locked out");
  694. else
  695. throw new ProviderException ("Failed to reset password");
  696. }
  697. }
  698. public override void UpdateUser (MembershipUser user)
  699. {
  700. if (user == null)
  701. throw new ArgumentNullException ("user");
  702. if (user.UserName == null)
  703. throw new ArgumentNullException ("user.UserName");
  704. if (RequiresUniqueEmail && user.Email == null)
  705. throw new ArgumentNullException ("user.Email");
  706. CheckParam ("user.UserName", user.UserName, 256);
  707. if (user.Email.Length > 256 || (RequiresUniqueEmail && user.Email.Length == 0))
  708. throw new ArgumentException ("invalid format for user.Email");
  709. using (DbConnection connection = CreateConnection ()) {
  710. int returnValue = 0;
  711. DbCommand command = factory.CreateCommand ();
  712. command.Connection = connection;
  713. command.CommandText = @"aspnet_Membership_UpdateUser";
  714. command.CommandType = CommandType.StoredProcedure;
  715. AddParameter (command, "@ApplicationName", ApplicationName);
  716. AddParameter (command, "@UserName", user.UserName);
  717. AddParameter (command, "@Email", user.Email == null ? (object) DBNull.Value : (object) user.Email);
  718. AddParameter (command, "@Comment", user.Comment == null ? (object) DBNull.Value : (object) user.Comment);
  719. AddParameter (command, "@IsApproved", user.IsApproved);
  720. AddParameter (command, "@LastLoginDate", DateTime.UtcNow);
  721. AddParameter (command, "@LastActivityDate", DateTime.UtcNow);
  722. AddParameter (command, "@UniqueEmail", RequiresUniqueEmail);
  723. AddParameter (command, "@CurrentTimeUtc", DateTime.UtcNow);
  724. DbParameter retValue = AddParameter (command, "@ReturnVal", ParameterDirection.ReturnValue, DbType.Int32, null);
  725. command.ExecuteNonQuery ();
  726. returnValue = GetReturnValue (retValue);
  727. if (returnValue == 1)
  728. throw new ProviderException ("The UserName property of user was not found in the database.");
  729. if (returnValue == 7)
  730. 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.");
  731. if (returnValue != 0)
  732. throw new ProviderException ("Failed to update user");
  733. }
  734. }
  735. public override bool ValidateUser (string username, string password)
  736. {
  737. if (username.Length == 0)
  738. return false;
  739. CheckParam ("username", username, 256);
  740. EmitValidatingPassword (username, password, false);
  741. PasswordInfo pi = ValidateUsingPassword (username, password);
  742. if (pi != null) {
  743. pi.LastLoginDate = DateTime.UtcNow;
  744. UpdateUserInfo (username, pi, true, true);
  745. return true;
  746. }
  747. return false;
  748. }
  749. public override bool UnlockUser (string username)
  750. {
  751. CheckParam ("username", username, 256);
  752. using (DbConnection connection = CreateConnection ()) {
  753. try {
  754. DbCommand command = factory.CreateCommand ();
  755. command.Connection = connection;
  756. command.CommandText = @"aspnet_Membership_UnlockUser"; ;
  757. command.CommandType = CommandType.StoredProcedure;
  758. AddParameter (command, "@ApplicationName", ApplicationName);
  759. AddParameter (command, "@UserName", username);
  760. DbParameter returnValue = AddParameter (command, "@ReturnVal", ParameterDirection.ReturnValue, DbType.Int32, null);
  761. command.ExecuteNonQuery ();
  762. if (GetReturnValue (returnValue) != 0)
  763. return false;
  764. }
  765. catch (Exception e) {
  766. throw new ProviderException ("Failed to unlock user", e);
  767. }
  768. }
  769. return true;
  770. }
  771. void UpdateUserInfo (string username, PasswordInfo pi, bool isPasswordCorrect, bool updateLoginActivity)
  772. {
  773. CheckParam ("username", username, 256);
  774. using (DbConnection connection = CreateConnection ()) {
  775. try {
  776. DbCommand command = factory.CreateCommand ();
  777. command.Connection = connection;
  778. command.CommandText = @"aspnet_Membership_UpdateUserInfo"; ;
  779. command.CommandType = CommandType.StoredProcedure;
  780. AddParameter (command, "@ApplicationName", ApplicationName);
  781. AddParameter (command, "@UserName", username);
  782. AddParameter (command, "@IsPasswordCorrect", isPasswordCorrect);
  783. AddParameter (command, "@UpdateLastLoginActivityDate", updateLoginActivity);
  784. AddParameter (command, "@MaxInvalidPasswordAttempts", MaxInvalidPasswordAttempts);
  785. AddParameter (command, "@PasswordAttemptWindow", PasswordAttemptWindow);
  786. AddParameter (command, "@CurrentTimeUtc", DateTime.UtcNow);
  787. AddParameter (command, "@LastLoginDate", pi.LastLoginDate);
  788. AddParameter (command, "@LastActivityDate", pi.LastActivityDate);
  789. DbParameter retValue = AddParameter (command, "@ReturnVal", ParameterDirection.ReturnValue, DbType.Int32, null);
  790. command.ExecuteNonQuery ();
  791. int returnValue = GetReturnValue (retValue);
  792. if (returnValue != 0)
  793. return;
  794. }
  795. catch (Exception e) {
  796. throw new ProviderException ("Failed to update Membership table", e);
  797. }
  798. }
  799. }
  800. PasswordInfo ValidateUsingPassword (string username, string password)
  801. {
  802. MembershipUser user = GetUser (username, true);
  803. if (user == null)
  804. return null;
  805. if (!user.IsApproved || user.IsLockedOut)
  806. return null;
  807. PasswordInfo pi = GetPasswordInfo (username);
  808. if (pi == null)
  809. return null;
  810. /* do the actual validation */
  811. string user_password = EncodePassword (password, pi.PasswordFormat, pi.PasswordSalt);
  812. if (user_password != pi.Password) {
  813. UpdateUserInfo (username, pi, false, false);
  814. return null;
  815. }
  816. return pi;
  817. }
  818. PasswordInfo GetPasswordInfo (string username)
  819. {
  820. using (DbConnection connection = CreateConnection ()) {
  821. DbCommand command = factory.CreateCommand ();
  822. command.Connection = connection;
  823. command.CommandType = CommandType.StoredProcedure;
  824. command.CommandText = @"aspnet_Membership_GetPasswordWithFormat";
  825. AddParameter (command, "@ApplicationName", ApplicationName);
  826. AddParameter (command, "@UserName", username);
  827. AddParameter (command, "@UpdateLastLoginActivityDate", false);
  828. AddParameter (command, "@CurrentTimeUtc", DateTime.Now);
  829. // return value
  830. AddParameter (command, "@ReturnVal", ParameterDirection.ReturnValue, DbType.Int32, null);
  831. DbDataReader reader = command.ExecuteReader ();
  832. if (!reader.Read ())
  833. return null;
  834. PasswordInfo pi = new PasswordInfo (
  835. reader.GetString (0),
  836. (MembershipPasswordFormat) reader.GetInt32 (1),
  837. reader.GetString (2),
  838. reader.GetInt32 (3),
  839. reader.GetInt32 (4),
  840. reader.GetBoolean (5),
  841. reader.GetDateTime (6),
  842. reader.GetDateTime (7));
  843. return pi;
  844. }
  845. }
  846. string EncodePassword (string password, MembershipPasswordFormat passwordFormat, string salt)
  847. {
  848. byte [] password_bytes;
  849. byte [] salt_bytes;
  850. switch (passwordFormat) {
  851. case MembershipPasswordFormat.Clear:
  852. return password;
  853. case MembershipPasswordFormat.Hashed:
  854. password_bytes = Encoding.Unicode.GetBytes (password);
  855. salt_bytes = Convert.FromBase64String (salt);
  856. byte [] hashBytes = new byte [salt_bytes.Length + password_bytes.Length];
  857. Buffer.BlockCopy (salt_bytes, 0, hashBytes, 0, salt_bytes.Length);
  858. Buffer.BlockCopy (password_bytes, 0, hashBytes, salt_bytes.Length, password_bytes.Length);
  859. MembershipSection section = (MembershipSection) WebConfigurationManager.GetSection ("system.web/membership");
  860. string alg_type = section.HashAlgorithmType;
  861. if (alg_type.Length == 0) {
  862. alg_type = MachineKeySection.Config.Validation.ToString ();
  863. #if NET_4_0
  864. // support new (4.0) custom algorithms
  865. if (alg_type.StartsWith ("alg:"))
  866. alg_type = alg_type.Substring (4);
  867. #endif
  868. }
  869. using (HashAlgorithm hash = HashAlgorithm.Create (alg_type)) {
  870. #if NET_4_0
  871. // for compatibility (with 2.0) we'll allow MD5 and SHA1 not to map to HMACMD5 and HMACSHA1
  872. // but that won't work with new (4.0) algorithms, like HMACSHA256|384|512 or custom, won't work without using the key
  873. KeyedHashAlgorithm kha = (hash as KeyedHashAlgorithm);
  874. if (kha != null)
  875. kha.Key = MachineKeySection.Config.GetValidationKey ();
  876. #endif
  877. hash.TransformFinalBlock (hashBytes, 0, hashBytes.Length);
  878. return Convert.ToBase64String (hash.Hash);
  879. }
  880. case MembershipPasswordFormat.Encrypted:
  881. password_bytes = Encoding.Unicode.GetBytes (password);
  882. salt_bytes = Convert.FromBase64String (salt);
  883. byte [] buf = new byte [password_bytes.Length + salt_bytes.Length];
  884. Array.Copy (salt_bytes, 0, buf, 0, salt_bytes.Length);
  885. Array.Copy (password_bytes, 0, buf, salt_bytes.Length, password_bytes.Length);
  886. return Convert.ToBase64String (EncryptPassword (buf));
  887. default:
  888. /* not reached.. */
  889. return null;
  890. }
  891. }
  892. string DecodePassword (string password, MembershipPasswordFormat passwordFormat)
  893. {
  894. switch (passwordFormat) {
  895. case MembershipPasswordFormat.Clear:
  896. return password;
  897. case MembershipPasswordFormat.Hashed:
  898. throw new ProviderException ("Hashed passwords cannot be decoded.");
  899. case MembershipPasswordFormat.Encrypted:
  900. return Encoding.Unicode.GetString (DecryptPassword (Convert.FromBase64String (password)));
  901. default:
  902. /* not reached.. */
  903. return null;
  904. }
  905. }
  906. public override string ApplicationName
  907. {
  908. get { return applicationName; }
  909. set { applicationName = value; }
  910. }
  911. public override bool EnablePasswordReset
  912. {
  913. get { return enablePasswordReset; }
  914. }
  915. public override bool EnablePasswordRetrieval
  916. {
  917. get { return enablePasswordRetrieval; }
  918. }
  919. public override MembershipPasswordFormat PasswordFormat
  920. {
  921. get { return passwordFormat; }
  922. }
  923. public override bool RequiresQuestionAndAnswer
  924. {
  925. get { return requiresQuestionAndAnswer; }
  926. }
  927. public override bool RequiresUniqueEmail
  928. {
  929. get { return requiresUniqueEmail; }
  930. }
  931. public override int MaxInvalidPasswordAttempts
  932. {
  933. get { return maxInvalidPasswordAttempts; }
  934. }
  935. public override int MinRequiredNonAlphanumericCharacters
  936. {
  937. get { return minRequiredNonAlphanumericCharacters; }
  938. }
  939. public override int MinRequiredPasswordLength
  940. {
  941. get { return minRequiredPasswordLength; }
  942. }
  943. public override int PasswordAttemptWindow
  944. {
  945. get { return passwordAttemptWindow; }
  946. }
  947. public override string PasswordStrengthRegularExpression
  948. {
  949. get { return passwordStrengthRegularExpression; }
  950. }
  951. [Flags]
  952. enum DeleteUserTableMask
  953. {
  954. MembershipUsers = 1,
  955. UsersInRoles = 2,
  956. Profiles = 4,
  957. WebPartStateUser = 8
  958. }
  959. sealed class PasswordInfo
  960. {
  961. string _password;
  962. MembershipPasswordFormat _passwordFormat;
  963. string _passwordSalt;
  964. int _failedPasswordAttemptCount;
  965. int _failedPasswordAnswerAttemptCount;
  966. bool _isApproved;
  967. DateTime _lastLoginDate;
  968. DateTime _lastActivityDate;
  969. internal PasswordInfo (
  970. string password,
  971. MembershipPasswordFormat passwordFormat,
  972. string passwordSalt,
  973. int failedPasswordAttemptCount,
  974. int failedPasswordAnswerAttemptCount,
  975. bool isApproved,
  976. DateTime lastLoginDate,
  977. DateTime lastActivityDate)
  978. {
  979. _password = password;
  980. _passwordFormat = passwordFormat;
  981. _passwordSalt = passwordSalt;
  982. _failedPasswordAttemptCount = failedPasswordAttemptCount;
  983. _failedPasswordAnswerAttemptCount = failedPasswordAnswerAttemptCount;
  984. _isApproved = isApproved;
  985. _lastLoginDate = lastLoginDate;
  986. _lastActivityDate = lastActivityDate;
  987. }
  988. public string Password
  989. {
  990. get { return _password; }
  991. set { _password = value; }
  992. }
  993. public MembershipPasswordFormat PasswordFormat
  994. {
  995. get { return _passwordFormat; }
  996. set { _passwordFormat = value; }
  997. }
  998. public string PasswordSalt
  999. {
  1000. get { return _passwordSalt; }
  1001. set { _passwordSalt = value; }
  1002. }
  1003. public int FailedPasswordAttemptCount
  1004. {
  1005. get { return _failedPasswordAttemptCount; }
  1006. set { _failedPasswordAttemptCount = value; }
  1007. }
  1008. public int FailedPasswordAnswerAttemptCount
  1009. {
  1010. get { return _failedPasswordAnswerAttemptCount; }
  1011. set { _failedPasswordAnswerAttemptCount = value; }
  1012. }
  1013. public bool IsApproved
  1014. {
  1015. get { return _isApproved; }
  1016. set { _isApproved = value; }
  1017. }
  1018. public DateTime LastLoginDate
  1019. {
  1020. get { return _lastLoginDate; }
  1021. set { _lastLoginDate = value; }
  1022. }
  1023. public DateTime LastActivityDate
  1024. {
  1025. get { return _lastActivityDate; }
  1026. set { _lastActivityDate = value; }
  1027. }
  1028. }
  1029. }
  1030. }
  1031. #endif