3 // Permission is hereby granted, free of charge, to any person obtaining
4 // a copy of this software and associated documentation files (the
5 // "Software"), to deal in the Software without restriction, including
6 // without limitation the rights to use, copy, modify, merge, publish,
7 // distribute, sublicense, and/or sell copies of the Software, and to
8 // permit persons to whom the Software is furnished to do so, subject to
9 // the following conditions:
11 // The above copyright notice and this permission notice shall be
12 // included in all copies or substantial portions of the Software.
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 // Copyright © 2006, 2007 Nauck IT KG http://www.nauck-it.de
25 // Daniel Nauck <d.nauck(at)nauck-it.de>
27 // Adapted to Sqlite by Marek Habersack <mhabersack@novell.com>
33 using System.Data.Common;
34 using System.Collections.Generic;
35 using System.Collections.Specialized;
37 using System.Security.Cryptography;
38 using System.Web.Hosting;
39 using System.Web.Configuration;
40 using System.Web.Security;
41 using System.Configuration;
42 using System.Configuration.Provider;
43 using System.Diagnostics;
45 using Mono.Data.Sqlite;
47 namespace System.Web.Security
49 internal class SqliteMembershipProvider : MembershipProvider
51 const string m_TableName = "Users";
52 string m_ConnectionString = string.Empty;
53 const int m_NewPasswordLength = 8;
54 bool machineKeyIsAutoGenerated;
56 // Used when determining encryption key values.
57 MachineKeySection m_MachineKey = null;
59 DbParameter AddParameter (DbCommand command, string parameterName, object parameterValue)
61 return AddParameter (command, parameterName, ParameterDirection.Input, parameterValue);
64 DbParameter AddParameter (DbCommand command, string parameterName, ParameterDirection direction, object parameterValue)
66 DbParameter dbp = command.CreateParameter ();
67 dbp.ParameterName = parameterName;
68 dbp.Value = parameterValue;
69 dbp.Direction = direction;
70 command.Parameters.Add (dbp);
75 /// System.Configuration.Provider.ProviderBase.Initialize Method.
77 public override void Initialize(string name, NameValueCollection config)
79 // Initialize values from web.config.
81 throw new ArgumentNullException("Config", Properties.Resources.ErrArgumentNull);
83 if (string.IsNullOrEmpty(name))
84 name = Properties.Resources.MembershipProviderDefaultName;
86 if (string.IsNullOrEmpty(config["description"]))
88 config.Remove("description");
89 config.Add("description", Properties.Resources.MembershipProviderDefaultDescription);
92 // Initialize the abstract base class.
93 base.Initialize(name, config);
95 m_ApplicationName = GetConfigValue(config["applicationName"], HostingEnvironment.ApplicationVirtualPath);
96 m_MaxInvalidPasswordAttempts = Convert.ToInt32(GetConfigValue(config["maxInvalidPasswordAttempts"], "5"));
97 m_PasswordAttemptWindow = Convert.ToInt32(GetConfigValue(config["passwordAttemptWindow"], "10"));
98 m_MinRequiredNonAlphanumericCharacters = Convert.ToInt32(GetConfigValue(config["minRequiredNonAlphanumericCharacters"], "1"));
99 m_MinRequiredPasswordLength = Convert.ToInt32(GetConfigValue(config["minRequiredPasswordLength"], "7"));
100 m_PasswordStrengthRegularExpression = Convert.ToString(GetConfigValue(config["passwordStrengthRegularExpression"], ""));
101 m_EnablePasswordReset = Convert.ToBoolean(GetConfigValue(config["enablePasswordReset"], "true"));
102 m_EnablePasswordRetrieval = Convert.ToBoolean(GetConfigValue(config["enablePasswordRetrieval"], "true"));
103 m_RequiresQuestionAndAnswer = Convert.ToBoolean(GetConfigValue(config["requiresQuestionAndAnswer"], "false"));
104 m_RequiresUniqueEmail = Convert.ToBoolean(GetConfigValue(config["requiresUniqueEmail"], "true"));
106 // Get password encryption type.
107 string pwFormat = GetConfigValue(config["passwordFormat"], "Hashed");
111 m_PasswordFormat = MembershipPasswordFormat.Hashed;
114 m_PasswordFormat = MembershipPasswordFormat.Encrypted;
117 m_PasswordFormat = MembershipPasswordFormat.Clear;
120 throw new ProviderException(Properties.Resources.ErrPwFormatNotSupported);
123 // Get connection string.
124 string connStrName = config["connectionStringName"];
126 if (string.IsNullOrEmpty(connStrName))
128 throw new ArgumentOutOfRangeException("ConnectionStringName", Properties.Resources.ErrArgumentNullOrEmpty);
132 ConnectionStringSettings ConnectionStringSettings = ConfigurationManager.ConnectionStrings[connStrName];
134 if (ConnectionStringSettings == null || string.IsNullOrEmpty(ConnectionStringSettings.ConnectionString.Trim()))
136 throw new ProviderException(Properties.Resources.ErrConnectionStringNullOrEmpty);
139 m_ConnectionString = ConnectionStringSettings.ConnectionString;
142 // Get encryption and decryption key information from the configuration.
143 System.Configuration.Configuration cfg = WebConfigurationManager.OpenWebConfiguration(HostingEnvironment.ApplicationVirtualPath);
144 m_MachineKey = (MachineKeySection)cfg.GetSection("system.web/machineKey");
146 if (!m_PasswordFormat.Equals(MembershipPasswordFormat.Clear))
148 if (m_MachineKey == null)
149 throw new ArgumentNullException("system.web/machineKey", Properties.Resources.ErrArgumentNull);
151 if (m_MachineKey.ValidationKey.Contains("AutoGenerate")) {
152 machineKeyIsAutoGenerated = true;
153 if (m_PasswordFormat.Equals (MembershipPasswordFormat.Encrypted))
154 throw new ProviderException(Properties.Resources.ErrAutoGeneratedKeyNotSupported);
160 /// System.Web.Security.MembershipProvider properties.
162 #region System.Web.Security.MembershipProvider properties
163 string m_ApplicationName = string.Empty;
164 bool m_EnablePasswordReset = false;
165 bool m_EnablePasswordRetrieval = false;
166 bool m_RequiresQuestionAndAnswer = false;
167 bool m_RequiresUniqueEmail = false;
168 int m_MaxInvalidPasswordAttempts = 0;
169 int m_PasswordAttemptWindow = 0;
170 MembershipPasswordFormat m_PasswordFormat = MembershipPasswordFormat.Clear;
171 int m_MinRequiredNonAlphanumericCharacters = 0;
172 int m_MinRequiredPasswordLength = 0;
173 string m_PasswordStrengthRegularExpression = string.Empty;
175 public override string ApplicationName
177 get { return m_ApplicationName; }
178 set { m_ApplicationName = value; }
181 public override bool EnablePasswordReset
183 get { return m_EnablePasswordReset; }
186 public override bool EnablePasswordRetrieval
188 get { return m_EnablePasswordRetrieval; }
191 public override bool RequiresQuestionAndAnswer
193 get { return m_RequiresQuestionAndAnswer; }
196 public override bool RequiresUniqueEmail
198 get { return m_RequiresUniqueEmail; }
201 public override int MaxInvalidPasswordAttempts
203 get { return m_MaxInvalidPasswordAttempts; }
206 public override int PasswordAttemptWindow
208 get { return m_PasswordAttemptWindow; }
211 public override MembershipPasswordFormat PasswordFormat
213 get { return m_PasswordFormat; }
216 public override int MinRequiredNonAlphanumericCharacters
218 get { return m_MinRequiredNonAlphanumericCharacters; }
221 public override int MinRequiredPasswordLength
223 get { return m_MinRequiredPasswordLength; }
226 public override string PasswordStrengthRegularExpression
228 get { return m_PasswordStrengthRegularExpression; }
234 /// System.Web.Security.MembershipProvider methods.
236 #region System.Web.Security.MembershipProvider methods
239 /// MembershipProvider.ChangePassword
241 public override bool ChangePassword(string username, string oldPassword, string newPassword)
243 if (!ValidateUser(username, oldPassword))
246 ValidatePasswordEventArgs args = new ValidatePasswordEventArgs(username, newPassword, true);
248 OnValidatingPassword(args);
252 if (args.FailureInformation != null)
253 throw args.FailureInformation;
255 throw new MembershipPasswordException(Properties.Resources.ErrPasswordChangeCanceled);
258 int rowsAffected = 0;
260 using (SqliteConnection dbConn = new SqliteConnection(m_ConnectionString))
262 using (SqliteCommand dbCommand = dbConn.CreateCommand())
264 dbCommand.CommandText = string.Format("UPDATE \"{0}\" SET \"Password\" = @Password, \"LastPasswordChangedDate\" = @LastPasswordChangedDate WHERE \"Username\" = @Username AND \"ApplicationName\" = @ApplicationName", m_TableName);
266 AddParameter (dbCommand,"@Password", EncodePassword(newPassword));
267 AddParameter (dbCommand,"@LastPasswordChangedDate", DateTime.Now);
268 AddParameter (dbCommand,"@Username", username);
269 AddParameter (dbCommand,"@ApplicationName", m_ApplicationName);
276 rowsAffected = dbCommand.ExecuteNonQuery();
278 catch (SqliteException e)
280 Trace.WriteLine(e.ToString());
281 throw new ProviderException(Properties.Resources.ErrOperationAborted);
291 if (rowsAffected > 0)
298 /// MembershipProvider.ChangePasswordQuestionAndAnswer
300 public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer)
302 if (!ValidateUser(username, password))
305 int rowsAffected = 0;
307 using (SqliteConnection dbConn = new SqliteConnection(m_ConnectionString))
309 using (SqliteCommand dbCommand = dbConn.CreateCommand())
311 dbCommand.CommandText = string.Format("UPDATE \"{0}\" SET \"PasswordQuestion\" = @PasswordQuestion, \"PasswordAnswer\" = @PasswordAnswer WHERE \"Username\" = @Username AND \"ApplicationName\" = @ApplicationName", m_TableName);
313 AddParameter (dbCommand,"@PasswordQuestion", newPasswordQuestion);
314 AddParameter (dbCommand,"@PasswordAnswer", EncodePassword(newPasswordAnswer));
315 AddParameter (dbCommand,"@Username", username);
316 AddParameter (dbCommand,"@ApplicationName", m_ApplicationName);
323 rowsAffected = dbCommand.ExecuteNonQuery();
325 catch (SqliteException e)
327 Trace.WriteLine(e.ToString());
328 throw new ProviderException(Properties.Resources.ErrOperationAborted);
338 if (rowsAffected > 0)
345 /// MembershipProvider.CreateUser
347 public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved,
348 object providerUserKey, out MembershipCreateStatus status)
350 ValidatePasswordEventArgs args = new ValidatePasswordEventArgs(username, password, true);
352 OnValidatingPassword(args);
356 status = MembershipCreateStatus.InvalidPassword;
360 if (RequiresUniqueEmail && string.IsNullOrEmpty(email))
362 status = MembershipCreateStatus.InvalidEmail;
366 if (RequiresUniqueEmail && !string.IsNullOrEmpty(GetUserNameByEmail(email)))
368 status = MembershipCreateStatus.DuplicateEmail;
372 if (GetUser(username, false) == null)
374 DateTime createDate = DateTime.Now;
376 if (providerUserKey == null)
378 providerUserKey = Guid.NewGuid();
382 if (!(providerUserKey is Guid))
384 status = MembershipCreateStatus.InvalidProviderUserKey;
389 // Create user in database
390 using (SqliteConnection dbConn = new SqliteConnection(m_ConnectionString))
392 using (SqliteCommand dbCommand = dbConn.CreateCommand())
394 dbCommand.CommandText = string.Format("INSERT INTO \"{0}\" (\"pId\", \"Username\", \"Password\", \"Email\", \"PasswordQuestion\", \"PasswordAnswer\", \"IsApproved\", \"CreationDate\", \"LastPasswordChangedDate\", \"LastActivityDate\", \"ApplicationName\", \"IsLockedOut\", \"LastLockedOutDate\", \"FailedPasswordAttemptCount\", \"FailedPasswordAttemptWindowStart\", \"FailedPasswordAnswerAttemptCount\", \"FailedPasswordAnswerAttemptWindowStart\") Values (@pId, @Username, @Password, @Email, @PasswordQuestion, @PasswordAnswer, @IsApproved, @CreationDate, @LastPasswordChangedDate, @LastActivityDate, @ApplicationName, @IsLockedOut, @LastLockedOutDate, @FailedPasswordAttemptCount, @FailedPasswordAttemptWindowStart, @FailedPasswordAnswerAttemptCount, @FailedPasswordAnswerAttemptWindowStart)", m_TableName);
396 AddParameter (dbCommand,"@pId", providerUserKey);
397 AddParameter (dbCommand,"@Username", username);
398 AddParameter (dbCommand,"@Password", EncodePassword(password));
399 AddParameter (dbCommand,"@Email", email);
400 AddParameter (dbCommand,"@PasswordQuestion", passwordQuestion);
401 AddParameter (dbCommand,"@PasswordAnswer", EncodePassword(passwordAnswer));
402 AddParameter (dbCommand,"@IsApproved", isApproved);
403 AddParameter (dbCommand,"@CreationDate", createDate);
404 AddParameter (dbCommand,"@LastPasswordChangedDate", createDate);
405 AddParameter (dbCommand,"@LastActivityDate", createDate);
406 AddParameter (dbCommand,"@ApplicationName", m_ApplicationName);
407 AddParameter (dbCommand,"@IsLockedOut", false);
408 AddParameter (dbCommand,"@LastLockedOutDate", createDate);
409 AddParameter (dbCommand,"@FailedPasswordAttemptCount", 0);
410 AddParameter (dbCommand,"@FailedPasswordAttemptWindowStart", createDate);
411 AddParameter (dbCommand,"@FailedPasswordAnswerAttemptCount", 0);
412 AddParameter (dbCommand,"@FailedPasswordAnswerAttemptWindowStart", createDate);
419 if (dbCommand.ExecuteNonQuery() > 0)
421 status = MembershipCreateStatus.Success;
425 status = MembershipCreateStatus.UserRejected;
428 catch (SqliteException e)
430 status = MembershipCreateStatus.ProviderError;
431 Trace.WriteLine(e.ToString());
432 throw new ProviderException(Properties.Resources.ErrOperationAborted);
440 return GetUser(username, false);
446 status = MembershipCreateStatus.DuplicateUserName;
452 /// MembershipProvider.DeleteUser
454 public override bool DeleteUser(string username, bool deleteAllRelatedData)
456 int rowsAffected = 0;
458 using (SqliteConnection dbConn = new SqliteConnection(m_ConnectionString))
460 using (SqliteCommand dbCommand = dbConn.CreateCommand())
462 dbCommand.CommandText = string.Format("DELETE FROM \"{0}\" WHERE \"Username\" = @Username AND \"ApplicationName\" = @ApplicationName", m_TableName);
464 AddParameter (dbCommand,"@Username", username);
465 AddParameter (dbCommand,"@ApplicationName", m_ApplicationName);
472 rowsAffected = dbCommand.ExecuteNonQuery();
474 if (deleteAllRelatedData)
476 // Process commands to delete all data for the user in the database.
479 catch (SqliteException e)
481 Trace.WriteLine(e.ToString());
482 throw new ProviderException(Properties.Resources.ErrOperationAborted);
492 if (rowsAffected > 0)
499 /// MembershipProvider.FindUsersByEmail
501 public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
504 MembershipUserCollection users = new MembershipUserCollection();
506 // replace permitted wildcard characters
507 emailToMatch = emailToMatch.Replace('*','%');
508 emailToMatch = emailToMatch.Replace('?', '_');
510 using (SqliteConnection dbConn = new SqliteConnection(m_ConnectionString))
513 using (SqliteCommand dbCommand = dbConn.CreateCommand())
515 dbCommand.CommandText = string.Format("SELECT Count(*) FROM \"{0}\" WHERE \"Email\" LIKE @Email AND \"ApplicationName\" = @ApplicationName", m_TableName);
517 AddParameter (dbCommand,"@Email", emailToMatch);
518 AddParameter (dbCommand,"@ApplicationName", m_ApplicationName);
525 Int32.TryParse(dbCommand.ExecuteScalar().ToString(), out totalRecords);
527 if (totalRecords <= 0) { return users; }
529 catch (SqliteException e)
531 Trace.WriteLine(e.ToString());
532 throw new ProviderException(Properties.Resources.ErrOperationAborted);
541 // Fetch user from database
542 using (SqliteCommand dbCommand = dbConn.CreateCommand())
544 dbCommand.CommandText = string.Format("SELECT \"pId\", \"Username\", \"Email\", \"PasswordQuestion\", \"Comment\", \"IsApproved\", \"IsLockedOut\", \"CreationDate\", \"LastLoginDate\", \"LastActivityDate\", \"LastPasswordChangedDate\", \"LastLockedOutDate\" FROM \"{0}\" WHERE \"Email\" LIKE @Email AND \"ApplicationName\" = @ApplicationName ORDER BY \"Username\" ASC LIMIT @MaxCount OFFSET @StartIndex", m_TableName);
546 AddParameter (dbCommand,"@Email", emailToMatch);
547 AddParameter (dbCommand,"@ApplicationName", m_ApplicationName);
548 AddParameter (dbCommand,"@MaxCount", pageSize);
549 AddParameter (dbCommand,"@StartIndex", pageSize * pageIndex);
556 using (SqliteDataReader reader = dbCommand.ExecuteReader())
558 while (reader.Read())
560 MembershipUser u = GetUserFromReader(reader);
565 catch (SqliteException e)
567 Trace.WriteLine(e.ToString());
568 throw new ProviderException(Properties.Resources.ErrOperationAborted);
582 /// MembershipProvider.FindUsersByName
584 public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
587 MembershipUserCollection users = new MembershipUserCollection();
589 // replace permitted wildcard characters
590 usernameToMatch = usernameToMatch.Replace('*', '%');
591 usernameToMatch = usernameToMatch.Replace('?', '_');
593 using (SqliteConnection dbConn = new SqliteConnection(m_ConnectionString))
596 using (SqliteCommand dbCommand = dbConn.CreateCommand())
598 dbCommand.CommandText = string.Format("SELECT Count(*) FROM \"{0}\" WHERE \"Username\" LIKE @Username AND \"ApplicationName\" = @ApplicationName", m_TableName);
600 AddParameter (dbCommand,"@Username", usernameToMatch);
601 AddParameter (dbCommand,"@ApplicationName", m_ApplicationName);
608 Int32.TryParse(dbCommand.ExecuteScalar().ToString(), out totalRecords);
610 if (totalRecords <= 0) { return users; }
612 catch (SqliteException e)
614 Trace.WriteLine(e.ToString());
615 throw new ProviderException(Properties.Resources.ErrOperationAborted);
624 // Fetch user from database
625 using (SqliteCommand dbCommand = dbConn.CreateCommand())
627 dbCommand.CommandText = string.Format("SELECT \"pId\", \"Username\", \"Email\", \"PasswordQuestion\", \"Comment\", \"IsApproved\", \"IsLockedOut\", \"CreationDate\", \"LastLoginDate\", \"LastActivityDate\", \"LastPasswordChangedDate\", \"LastLockedOutDate\" FROM \"{0}\" WHERE \"Username\" LIKE @Username AND \"ApplicationName\" = @ApplicationName ORDER BY \"Username\" ASC LIMIT @MaxCount OFFSET @StartIndex", m_TableName);
629 AddParameter (dbCommand,"@Username", usernameToMatch);
630 AddParameter (dbCommand,"@ApplicationName", m_ApplicationName);
631 AddParameter (dbCommand,"@MaxCount", pageSize);
632 AddParameter (dbCommand,"@StartIndex", pageSize * pageIndex);
639 using (SqliteDataReader reader = dbCommand.ExecuteReader())
641 while (reader.Read())
643 MembershipUser u = GetUserFromReader(reader);
648 catch (SqliteException e)
650 Trace.WriteLine(e.ToString());
651 throw new ProviderException(Properties.Resources.ErrOperationAborted);
665 /// MembershipProvider.GetAllUsers
667 public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
670 MembershipUserCollection users = new MembershipUserCollection();
672 using (SqliteConnection dbConn = new SqliteConnection(m_ConnectionString))
675 using (SqliteCommand dbCommand = dbConn.CreateCommand())
677 dbCommand.CommandText = string.Format("SELECT Count(*) FROM \"{0}\" WHERE \"ApplicationName\" = @ApplicationName", m_TableName);
679 AddParameter (dbCommand,"@ApplicationName", m_ApplicationName);
686 Int32.TryParse(dbCommand.ExecuteScalar().ToString(), out totalRecords);
688 if (totalRecords <= 0) { return users; }
690 catch (SqliteException e)
692 Trace.WriteLine(e.ToString());
702 // Fetch user from database
703 using (SqliteCommand dbCommand = dbConn.CreateCommand())
705 dbCommand.CommandText = string.Format("SELECT \"pId\", \"Username\", \"Email\", \"PasswordQuestion\", \"Comment\", \"IsApproved\", \"IsLockedOut\", \"CreationDate\", \"LastLoginDate\", \"LastActivityDate\", \"LastPasswordChangedDate\", \"LastLockedOutDate\" FROM \"{0}\" WHERE \"ApplicationName\" = @ApplicationName ORDER BY \"Username\" ASC LIMIT @MaxCount OFFSET @StartIndex", m_TableName);
707 AddParameter (dbCommand,"@ApplicationName", m_ApplicationName);
708 AddParameter (dbCommand,"@MaxCount", pageSize);
709 AddParameter (dbCommand,"@StartIndex", pageSize * pageIndex);
716 using (SqliteDataReader reader = dbCommand.ExecuteReader())
718 while (reader.Read())
720 MembershipUser u = GetUserFromReader(reader);
725 catch (SqliteException e)
727 Trace.WriteLine(e.ToString());
728 throw new ProviderException(Properties.Resources.ErrOperationAborted);
742 /// MembershipProvider.GetNumberOfUsersOnline
744 public override int GetNumberOfUsersOnline()
748 using (SqliteConnection dbConn = new SqliteConnection(m_ConnectionString))
750 using (SqliteCommand dbCommand = dbConn.CreateCommand())
752 TimeSpan onlineSpan = new TimeSpan(0, System.Web.Security.Membership.UserIsOnlineTimeWindow, 0);
753 DateTime compareTime = DateTime.Now.Subtract(onlineSpan);
755 dbCommand.CommandText = string.Format("SELECT Count(*) FROM \"{0}\" WHERE \"LastActivityDate\" > @CompareTime AND \"ApplicationName\" = @ApplicationName", m_TableName);
757 AddParameter (dbCommand,"@CompareTime", compareTime);
758 AddParameter (dbCommand,"@ApplicationName", m_ApplicationName);
765 numOnline = (int)dbCommand.ExecuteScalar();
767 catch (SqliteException e)
769 Trace.WriteLine(e.ToString());
770 throw new ProviderException(Properties.Resources.ErrOperationAborted);
784 /// MembershipProvider.GetPassword
786 public override string GetPassword(string username, string answer)
788 if (!EnablePasswordRetrieval)
790 throw new ProviderException(Properties.Resources.ErrPasswordRetrievalNotEnabled);
793 if (PasswordFormat == MembershipPasswordFormat.Hashed)
795 throw new ProviderException(Properties.Resources.ErrCantRetrieveHashedPw);
798 using (SqliteConnection dbConn = new SqliteConnection(m_ConnectionString))
800 using (SqliteCommand dbCommand = dbConn.CreateCommand())
802 dbCommand.CommandText = string.Format("SELECT \"Password\", \"PasswordAnswer\", \"IsLockedOut\" FROM \"{0}\" WHERE \"Username\" = @Username AND \"ApplicationName\" = @ApplicationName", m_TableName);
804 AddParameter (dbCommand,"@Username", username);
805 AddParameter (dbCommand,"@ApplicationName", m_ApplicationName);
812 using (SqliteDataReader reader = dbCommand.ExecuteReader())
818 string password = reader.GetString(0);
819 string passwordAnswer = reader.GetString(1);
820 bool isLockedOut = reader.GetBoolean(2);
825 throw new MembershipPasswordException(Properties.Resources.ErrUserIsLoggedOut);
827 if (m_RequiresQuestionAndAnswer && !CheckPassword(answer, passwordAnswer))
829 UpdateFailureCount(username, FailureType.PasswordAnswer);
831 throw new MembershipPasswordException(Properties.Resources.ErrIncorrectPasswordAnswer);
834 if (m_PasswordFormat == MembershipPasswordFormat.Encrypted)
836 password = UnEncodePassword(password);
843 throw new MembershipPasswordException(Properties.Resources.ErrUserNotFound);
847 catch (SqliteException e)
849 Trace.WriteLine(e.ToString());
850 throw new ProviderException(Properties.Resources.ErrOperationAborted);
862 /// MembershipProvider.GetUser
864 public override MembershipUser GetUser(string username, bool userIsOnline)
866 MembershipUser u = null;
868 using (SqliteConnection dbConn = new SqliteConnection(m_ConnectionString))
870 using (SqliteCommand dbCommand = dbConn.CreateCommand())
872 dbCommand.CommandText = string.Format("SELECT \"pId\", \"Username\", \"Email\", \"PasswordQuestion\", \"Comment\", \"IsApproved\", \"IsLockedOut\", \"CreationDate\", \"LastLoginDate\", \"LastActivityDate\", \"LastPasswordChangedDate\", \"LastLockedOutDate\" FROM \"{0}\" WHERE \"Username\" = @Username AND \"ApplicationName\" = @ApplicationName", m_TableName);
874 AddParameter (dbCommand,"@Username", username);
875 AddParameter (dbCommand,"@ApplicationName", m_ApplicationName);
882 using (SqliteDataReader reader = dbCommand.ExecuteReader())
887 u = GetUserFromReader(reader);
891 // Update user online status
892 using (SqliteCommand dbUpdateCommand = dbConn.CreateCommand())
894 dbUpdateCommand.CommandText = string.Format("UPDATE \"{0}\" SET \"LastActivityDate\" = @LastActivityDate WHERE \"pId\" = @pId", m_TableName);
896 AddParameter (dbUpdateCommand, "@LastActivityDate", DateTime.Now);
897 AddParameter (dbUpdateCommand, "@pId", u.ProviderUserKey);
899 dbUpdateCommand.Prepare();
901 dbUpdateCommand.ExecuteNonQuery();
907 catch (SqliteException e)
909 Trace.WriteLine(e.ToString());
910 throw new ProviderException(Properties.Resources.ErrOperationAborted);
924 /// MembershipProvider.GetUser
926 public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)
928 MembershipUser u = null;
930 using (SqliteConnection dbConn = new SqliteConnection(m_ConnectionString))
932 using (SqliteCommand dbCommand = dbConn.CreateCommand())
934 dbCommand.CommandText = string.Format("SELECT \"pId\", \"Username\", \"Email\", \"PasswordQuestion\", \"Comment\", \"IsApproved\", \"IsLockedOut\", \"CreationDate\", \"LastLoginDate\", \"LastActivityDate\", \"LastPasswordChangedDate\", \"LastLockedOutDate\" FROM \"{0}\" WHERE \"pId\" = @pId", m_TableName);
936 AddParameter (dbCommand,"@pId", providerUserKey);
943 using (SqliteDataReader reader = dbCommand.ExecuteReader())
948 u = GetUserFromReader(reader);
952 // Update user online status
953 using (SqliteCommand dbUpdateCommand = dbConn.CreateCommand())
955 dbUpdateCommand.CommandText = string.Format("UPDATE \"{0}\" SET \"LastActivityDate\" = @LastActivityDate WHERE \"pId\" = @pId", m_TableName);
957 AddParameter (dbUpdateCommand, "@LastActivityDate", DateTime.Now);
958 AddParameter (dbUpdateCommand, "@pId", u.ProviderUserKey);
960 dbUpdateCommand.Prepare();
962 dbUpdateCommand.ExecuteNonQuery();
968 catch (SqliteException e)
970 Trace.WriteLine(e.ToString());
971 throw new ProviderException(Properties.Resources.ErrOperationAborted);
985 /// MembershipProvider.GetUserNameByEmail
987 public override string GetUserNameByEmail(string email)
989 string username = string.Empty;
991 using (SqliteConnection dbConn = new SqliteConnection(m_ConnectionString))
993 using (SqliteCommand dbCommand = dbConn.CreateCommand())
995 dbCommand.CommandText = string.Format("SELECT \"Username\" FROM \"{0}\" WHERE \"Email\" = @Email AND \"ApplicationName\" = @ApplicationName", m_TableName);
997 AddParameter (dbCommand,"@Email", email);
998 AddParameter (dbCommand,"@ApplicationName", m_ApplicationName);
1003 dbCommand.Prepare();
1005 username = (dbCommand.ExecuteScalar() as string) ?? string.Empty;
1007 catch (SqliteException e)
1009 Trace.WriteLine(e.ToString());
1010 throw new ProviderException(Properties.Resources.ErrOperationAborted);
1024 /// MembershipProvider.ResetPassword
1026 public override string ResetPassword(string username, string answer)
1028 if (!m_EnablePasswordReset)
1030 throw new NotSupportedException(Properties.Resources.ErrPasswordResetNotEnabled);
1033 if (string.IsNullOrEmpty(answer) && m_RequiresQuestionAndAnswer)
1035 UpdateFailureCount(username, FailureType.PasswordAnswer);
1037 throw new ProviderException(Properties.Resources.ErrPasswordAnswerRequired);
1040 string newPassword = Membership.GeneratePassword(m_NewPasswordLength, m_MinRequiredNonAlphanumericCharacters);
1043 ValidatePasswordEventArgs args = new ValidatePasswordEventArgs(username, newPassword, true);
1045 OnValidatingPassword(args);
1049 if (args.FailureInformation != null)
1050 throw args.FailureInformation;
1052 throw new MembershipPasswordException(Properties.Resources.ErrPasswordResetCanceled);
1055 int rowsAffected = 0;
1057 using (SqliteConnection dbConn = new SqliteConnection(m_ConnectionString))
1059 using (SqliteCommand dbCommand = dbConn.CreateCommand())
1061 dbCommand.CommandText = string.Format("SELECT \"PasswordAnswer\", \"IsLockedOut\" FROM \"{0}\" WHERE \"Username\" = @Username AND \"ApplicationName\" = @ApplicationName", m_TableName);
1063 AddParameter (dbCommand,"@Username", username);
1064 AddParameter (dbCommand,"@ApplicationName", m_ApplicationName);
1068 string passwordAnswer = string.Empty;
1071 dbCommand.Prepare();
1073 using (SqliteDataReader reader = dbCommand.ExecuteReader())
1079 passwordAnswer = reader.GetString(0);
1080 bool isLockedOut = reader.GetBoolean(1);
1085 throw new MembershipPasswordException(Properties.Resources.ErrUserIsLoggedOut);
1087 if (m_RequiresQuestionAndAnswer && !CheckPassword(answer, passwordAnswer))
1089 UpdateFailureCount(username, FailureType.PasswordAnswer);
1091 throw new MembershipPasswordException(Properties.Resources.ErrIncorrectPasswordAnswer);
1096 throw new MembershipPasswordException(Properties.Resources.ErrUserNotFound);
1101 using (SqliteCommand dbUpdateCommand = dbConn.CreateCommand())
1103 dbUpdateCommand.CommandText = string.Format("UPDATE \"{0}\" SET \"Password\" = @Password, \"LastPasswordChangedDate\" = @LastPasswordChangedDate WHERE \"Username\" = @Username AND \"ApplicationName\" = @ApplicationName AND \"IsLockedOut\" = @IsLockedOut", m_TableName);
1105 AddParameter (dbUpdateCommand, "@Password", EncodePassword(newPassword));
1106 AddParameter (dbUpdateCommand, "@LastPasswordChangedDate", DateTime.Now);
1107 AddParameter (dbUpdateCommand, "@Username", username);
1108 AddParameter (dbUpdateCommand, "@ApplicationName", m_ApplicationName);
1109 AddParameter (dbUpdateCommand, "@IsLockedOut", false);
1111 dbUpdateCommand.Prepare();
1113 rowsAffected = dbUpdateCommand.ExecuteNonQuery();
1117 catch (SqliteException e)
1119 Trace.WriteLine(e.ToString());
1120 throw new ProviderException(Properties.Resources.ErrOperationAborted);
1130 if (rowsAffected > 0)
1134 throw new MembershipPasswordException(Properties.Resources.ErrPasswordResetAborted);
1138 /// MembershipProvider.UnlockUser
1140 public override bool UnlockUser(string userName)
1142 int rowsAffected = 0;
1144 using (SqliteConnection dbConn = new SqliteConnection(m_ConnectionString))
1146 using (SqliteCommand dbCommand = dbConn.CreateCommand())
1148 dbCommand.CommandText = string.Format("UPDATE \"{0}\" SET \"IsLockedOut\" = @IsLockedOut, \"LastLockedOutDate\" = @LastLockedOutDate WHERE \"Username\" = @Username AND \"ApplicationName\" = @ApplicationName", m_TableName);
1150 AddParameter (dbCommand,"@IsLockedOut", false);
1151 AddParameter (dbCommand,"@LastLockedOutDate", DateTime.Now);
1152 AddParameter (dbCommand,"@Username", userName);
1153 AddParameter (dbCommand,"@ApplicationName", m_ApplicationName);
1158 dbCommand.Prepare();
1160 rowsAffected = dbCommand.ExecuteNonQuery();
1162 catch (SqliteException e)
1164 Trace.WriteLine(e.ToString());
1165 throw new ProviderException(Properties.Resources.ErrOperationAborted);
1175 if (rowsAffected > 0)
1183 /// MembershipProvider.UpdateUser
1185 public override void UpdateUser(MembershipUser user)
1187 using (SqliteConnection dbConn = new SqliteConnection(m_ConnectionString))
1189 using (SqliteCommand dbCommand = dbConn.CreateCommand())
1191 dbCommand.CommandText = string.Format("UPDATE \"{0}\" SET \"Email\" = @Email, \"Comment\" = @Comment, \"IsApproved\" = @IsApproved WHERE \"Username\" = @Username AND \"ApplicationName\" = @ApplicationName", m_TableName);
1193 AddParameter (dbCommand,"@Email", user.Email);
1194 AddParameter (dbCommand,"@Comment", user.Comment);
1195 AddParameter (dbCommand,"@IsApproved", user.IsApproved);
1196 AddParameter (dbCommand,"@Username", user.UserName);
1197 AddParameter (dbCommand,"@ApplicationName", m_ApplicationName);
1202 dbCommand.Prepare();
1204 dbCommand.ExecuteNonQuery();
1206 catch (SqliteException e)
1208 Trace.WriteLine(e.ToString());
1209 throw new ProviderException(Properties.Resources.ErrOperationAborted);
1221 /// MembershipProvider.ValidateUser
1223 public override bool ValidateUser(string username, string password)
1225 string dbPassword = string.Empty;
1226 bool dbIsApproved = false;
1228 using (SqliteConnection dbConn = new SqliteConnection(m_ConnectionString))
1230 // Fetch user data from database
1231 using (SqliteCommand dbCommand = dbConn.CreateCommand())
1233 dbCommand.CommandText = string.Format("SELECT \"Password\", \"IsApproved\" FROM \"{0}\" WHERE \"Username\" = @Username AND \"ApplicationName\" = @ApplicationName AND \"IsLockedOut\" = @IsLockedOut", m_TableName);
1235 AddParameter (dbCommand,"@Username", username);
1236 AddParameter (dbCommand,"@ApplicationName", m_ApplicationName);
1237 AddParameter (dbCommand,"@IsLockedOut", false);
1242 dbCommand.Prepare();
1244 using (SqliteDataReader reader = dbCommand.ExecuteReader())
1249 dbPassword = reader.GetString(0);
1250 dbIsApproved = reader.GetBoolean(1);
1258 catch (SqliteException e)
1260 Trace.WriteLine(e.ToString());
1261 throw new ProviderException(Properties.Resources.ErrOperationAborted);
1270 if (CheckPassword(password, dbPassword))
1274 // Update last login date
1275 using (SqliteCommand dbCommand = dbConn.CreateCommand())
1277 dbCommand.CommandText = string.Format("UPDATE \"{0}\" SET \"LastLoginDate\" = @LastLoginDate WHERE \"Username\" = @Username AND \"ApplicationName\" = @ApplicationName", m_TableName);
1279 AddParameter (dbCommand,"@LastLoginDate", DateTime.Now);
1280 AddParameter (dbCommand,"@Username", username);
1281 AddParameter (dbCommand,"@ApplicationName", m_ApplicationName);
1286 dbCommand.Prepare();
1288 dbCommand.ExecuteNonQuery();
1292 catch (SqliteException e)
1294 Trace.WriteLine(e.ToString());
1295 throw new ProviderException(Properties.Resources.ErrOperationAborted);
1311 #region private methods
1313 /// A helper function to retrieve config values from the configuration file.
1315 /// <param name="configValue"></param>
1316 /// <param name="defaultValue"></param>
1317 /// <returns></returns>
1318 string GetConfigValue(string configValue, string defaultValue)
1320 if (string.IsNullOrEmpty(configValue))
1321 return defaultValue;
1327 /// A helper function that takes the current row from the SqliteDataReader
1328 /// and hydrates a MembershipUser from the values. Called by the
1329 /// MembershipUser.GetUser implementation.
1331 /// <param name="reader">SqliteDataReader object</param>
1332 /// <returns>MembershipUser object</returns>
1333 MembershipUser GetUserFromReader(SqliteDataReader reader)
1335 object providerUserKey = reader.GetValue(0);
1336 string username = reader.GetString(1);
1337 string email = string.Empty;
1338 if (!reader.IsDBNull(2))
1339 email = reader.GetString(2);
1341 string passwordQuestion = string.Empty;
1342 if (!reader.IsDBNull(3))
1343 passwordQuestion = reader.GetString(3);
1345 string comment = string.Empty;
1346 if (!reader.IsDBNull(4))
1347 comment = reader.GetString(4);
1349 bool isApproved = reader.GetBoolean(5);
1350 bool isLockedOut = reader.GetBoolean(6);
1351 DateTime creationDate = reader.GetDateTime(7);
1353 DateTime lastLoginDate = new DateTime();
1354 if (!reader.IsDBNull(8))
1355 lastLoginDate = reader.GetDateTime(8);
1357 DateTime lastActivityDate = reader.GetDateTime(9);
1358 DateTime lastPasswordChangedDate = reader.GetDateTime(10);
1360 DateTime lastLockedOutDate = new DateTime();
1361 if (!reader.IsDBNull(11))
1362 lastLockedOutDate = reader.GetDateTime(11);
1364 MembershipUser u = new MembershipUser(this.Name,
1375 lastPasswordChangedDate,
1382 /// Compares password values based on the MembershipPasswordFormat.
1384 /// <param name="password"></param>
1385 /// <param name="dbpassword"></param>
1386 /// <returns></returns>
1387 bool CheckPassword(string password, string dbpassword)
1389 string pass1 = password;
1390 string pass2 = dbpassword;
1392 switch (PasswordFormat)
1394 case MembershipPasswordFormat.Encrypted:
1395 pass2 = UnEncodePassword(dbpassword);
1398 case MembershipPasswordFormat.Hashed:
1399 pass1 = EncodePassword(password);
1406 if (pass1.Equals(pass2))
1413 /// Encrypts, Hashes, or leaves the password clear based on the PasswordFormat.
1415 /// <param name="password"></param>
1416 /// <returns></returns>
1417 string EncodePassword(string password)
1419 if (string.IsNullOrEmpty(password))
1422 string encodedPassword = password;
1424 switch (PasswordFormat)
1426 case MembershipPasswordFormat.Clear:
1429 case MembershipPasswordFormat.Encrypted:
1430 encodedPassword = Convert.ToBase64String(EncryptPassword(Encoding.Unicode.GetBytes(password)));
1433 case MembershipPasswordFormat.Hashed:
1434 HMACSHA1 hash = new HMACSHA1();
1435 if (machineKeyIsAutoGenerated)
1436 hash.Key = MachineKeySectionUtils.ValidationKeyBytes ();
1438 hash.Key = HexToByte(m_MachineKey.ValidationKey);
1439 encodedPassword = Convert.ToBase64String(hash.ComputeHash(Encoding.Unicode.GetBytes(password)));
1443 throw new ProviderException(Properties.Resources.ErrPwFormatNotSupported);
1446 return encodedPassword;
1450 /// Decrypts or leaves the password clear based on the PasswordFormat.
1452 /// <param name="encodedPassword"></param>
1453 /// <returns></returns>
1454 string UnEncodePassword(string encodedPassword)
1456 string password = encodedPassword;
1458 switch (PasswordFormat)
1460 case MembershipPasswordFormat.Clear:
1463 case MembershipPasswordFormat.Encrypted:
1464 password = Encoding.Unicode.GetString(DecryptPassword(Convert.FromBase64String(password)));
1467 case MembershipPasswordFormat.Hashed:
1468 throw new ProviderException(Properties.Resources.ErrCantDecodeHashedPw);
1471 throw new ProviderException(Properties.Resources.ErrPwFormatNotSupported);
1478 /// Converts a hexadecimal string to a byte array. Used to convert encryption
1479 /// key values from the configuration.
1481 /// <param name="hexString"></param>
1482 /// <returns></returns>
1483 byte[] HexToByte(string hexString)
1485 byte[] returnBytes = new byte[hexString.Length / 2];
1486 for (int i = 0; i < returnBytes.Length; i++)
1487 returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
1493 /// A helper method that performs the checks and updates associated with
1494 /// password failure tracking.
1496 /// <param name="username"></param>
1497 /// <param name="failType"></param>
1498 void UpdateFailureCount(string username, FailureType failType)
1500 DateTime windowStart = new DateTime();
1501 int failureCount = 0;
1503 using (SqliteConnection dbConn = new SqliteConnection(m_ConnectionString))
1505 // Fetch user data from database
1506 using (SqliteCommand dbCommand = dbConn.CreateCommand())
1508 dbCommand.CommandText = string.Format("SELECT \"FailedPasswordAttemptCount\", \"FailedPasswordAttemptWindowStart\", \"FailedPasswordAnswerAttemptCount\", \"FailedPasswordAnswerAttemptWindowStart\" FROM \"{0}\" WHERE \"Username\" = @Username AND \"ApplicationName\" = @ApplicationName", m_TableName);
1510 AddParameter (dbCommand,"@Username", username);
1511 AddParameter (dbCommand,"@ApplicationName", m_ApplicationName);
1516 dbCommand.Prepare();
1518 using (SqliteDataReader reader = dbCommand.ExecuteReader())
1524 if (failType.Equals(FailureType.Password))
1526 failureCount = reader.GetInt32(0);
1527 windowStart = reader.GetDateTime(1);
1529 else if (failType.Equals(FailureType.PasswordAnswer))
1531 failureCount = reader.GetInt32(2);
1532 windowStart = reader.GetDateTime(3);
1537 catch (SqliteException e)
1539 Trace.WriteLine(e.ToString());
1540 throw new ProviderException(Properties.Resources.ErrOperationAborted);
1549 // Calculate failture count and update database
1550 using (SqliteCommand dbCommand = dbConn.CreateCommand())
1552 DateTime windowEnd = windowStart.AddMinutes(m_PasswordAttemptWindow);
1556 if (failureCount == 0 || DateTime.Now > windowEnd)
1558 // First password failure or outside of PasswordAttemptWindow.
1559 // Start a new password failure count from 1 and a new window starting now.
1561 if (failType.Equals(FailureType.Password))
1563 dbCommand.CommandText = string.Format("UPDATE \"{0}\" SET \"FailedPasswordAttemptCount\" = @Count, \"FailedPasswordAttemptWindowStart\" = @WindowStart WHERE \"Username\" = @Username AND \"ApplicationName\" = @ApplicationName", m_TableName);
1565 else if (failType.Equals(FailureType.PasswordAnswer))
1567 dbCommand.CommandText = string.Format("UPDATE \"{0}\" SET \"FailedPasswordAnswerAttemptCount\" = @Count, \"FailedPasswordAnswerAttemptWindowStart\" = @WindowStart WHERE \"Username\" = @Username AND \"ApplicationName\" = @ApplicationName", m_TableName);
1570 AddParameter (dbCommand,"@Count", 1);
1571 AddParameter (dbCommand,"@WindowStart", DateTime.Now);
1572 AddParameter (dbCommand,"@Username", username);
1573 AddParameter (dbCommand,"@ApplicationName", m_ApplicationName);
1575 if (dbCommand.ExecuteNonQuery() < 0)
1576 throw new ProviderException(Properties.Resources.ErrCantUpdateFailtureCountAndWindowStart);
1582 if (failureCount >= m_MaxInvalidPasswordAttempts)
1584 // Password attempts have exceeded the failure threshold. Lock out
1586 dbCommand.CommandText = string.Format("UPDATE \"{0}\" SET \"IsLockedOut\" = @IsLockedOut, \"LastLockedOutDate\" = @LastLockedOutDate WHERE \"Username\" = @Username AND \"ApplicationName\" = @ApplicationName", m_TableName);
1588 AddParameter (dbCommand,"@IsLockedOut", true);
1589 AddParameter (dbCommand,"@LastLockedOutDate", DateTime.Now);
1590 AddParameter (dbCommand,"@Username", username);
1591 AddParameter (dbCommand,"@ApplicationName", m_ApplicationName);
1593 if (dbCommand.ExecuteNonQuery() < 0)
1594 throw new ProviderException(string.Format(Properties.Resources.ErrCantLogoutUser, username));
1598 // Password attempts have not exceeded the failure threshold. Update
1599 // the failure counts. Leave the window the same.
1600 if (failType.Equals(FailureType.Password))
1602 dbCommand.CommandText = string.Format("UPDATE \"{0}\" SET \"FailedPasswordAttemptCount\" = @Count WHERE \"Username\" = @Username AND \"ApplicationName\" = @ApplicationName", m_TableName);
1604 else if (failType.Equals(FailureType.PasswordAnswer))
1606 dbCommand.CommandText = string.Format("UPDATE \"{0}\" SET \"FailedPasswordAnswerAttemptCount\" = @Count WHERE \"Username\" = @Username AND \"ApplicationName\" = @ApplicationName", m_TableName);
1609 AddParameter (dbCommand,"@Count", failureCount);
1610 AddParameter (dbCommand,"@Username", username);
1611 AddParameter (dbCommand,"@ApplicationName", m_ApplicationName);
1613 if (dbCommand.ExecuteNonQuery() < 0)
1614 throw new ProviderException(Properties.Resources.ErrCantUpdateFailtureCount);
1618 catch (SqliteException e)
1620 Trace.WriteLine(e.ToString());
1621 throw new ProviderException(Properties.Resources.ErrOperationAborted);