SqlMembershipProvider.cs 41 KB

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