Here is my situation with code and error.
I have developed an asp.net 4.0 website with SQLite3 using visual studio and I am running
TechInfoSystems.Data.SQLiteProvider.dll
and
System.Data.SQLite.dll 1.0.66.0
I just recently decided I can't save passwords for user login as clear text in my database, so I am now hashing them using SHA1. Login still works great and now the passwords are saved as one-way hex values in the database which is what I want.
What this requires though is adequate password recovery. I put a link to reset password on the login.aspx page, this link takes you to ResetPassword.aspx which contains the asp.net control PasswordRecovery. I changed the UserName, question, and success parts into templates for a different format.
Now, When the User is redirected to ResetPassword.aspx, user is prompted to enter their username. Great! Then click submit button, and the question template arises to ask the user what the security question answer is. Great! here is the problem, if the user enters an incorrect password, the success page is not shown, and the page sits their processing. After ~20 seconds I see an error that says the database is locked. HERE IS THAT EXACT ERROR:
The database file is locked
database is locked
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.Data.SQLite.SQLiteException: The database file is locked
database is locked
Source Error:
An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.
Stack Trace:
[SQLiteException (0x80004005): The database file is locked
database is locked]
System.Data.SQLite.SQLite3.Step(SQLiteStatement stmt) +499
System.Data.SQLite.SQLiteDataReader.NextResult() +249
System.Data.SQLite.SQLiteDataReader..ctor(SQLiteCommand cmd, CommandBehavior behave) +79
System.Data.SQLite.SQLiteCommand.ExecuteReader(CommandBehavior behavior) +38
System.Data.SQLite.SQLiteCommand.ExecuteNonQuery() +39
TechInfoSystems.Data.SQLite.SQLiteMembershipProvider.UpdateFailureCount(String username, String failureType, Boolean isAuthenticated) +2265
TechInfoSystems.Data.SQLite.SQLiteMembershipProvider.ResetPassword(String username, String passwordAnswer) +1760
System.Web.Security.MembershipUser.ResetPassword(String passwordAnswer) +110
System.Web.Security.MembershipUser.ResetPassword(String passwordAnswer, Boolean useAnswer, Boolean throwOnError) +48
System.Web.UI.WebControls.PasswordRecovery.AttemptSendPasswordQuestionView() +374
System.Web.UI.WebControls.PasswordRecovery.AttemptSendPassword() +77
System.Web.UI.WebControls.PasswordRecovery.OnBubbleEvent(Object source, EventArgs e) +103
System.Web.UI.Control.RaiseBubbleEvent(Object source, EventArgs args) +37
System.Web.UI.WebControls.Button.OnCommand(CommandEventArgs e) +125
System.Web.UI.WebControls.Button.RaisePostBackEvent(String eventArgument) +167
System.Web.UI.WebControls.Button.System.Web.UI.IPostBackEventHandler.RaisePostBackEvent(String eventArgument) +10
System.Web.UI.Page.RaisePostBackEvent(IPostBackEventHandler sourceControl, String eventArgument) +13
System.Web.UI.Page.RaisePostBackEvent(NameValueCollection postData) +36
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +5563
HERE IS MY CODE:
<%@ Page Title="Change Password" Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true"
CodeFile="ResetPassword.aspx.cs" Inherits="Account_ChangePassword" %>
<asp:Content ID="HeaderContent" runat="server" ContentPlaceHolderID="HeadContent">
</asp:Content>
<asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent">
<h2>
Reset Password
</h2>
<asp:PasswordRecovery ID="PasswordRecovery" runat="server" Height="104px" Width="409px" Enabled="True" ViewStateMode="Disabled" MembershipProvider="SQLiteMembershipProvider">
<MailDefinition IsBodyHtml="True" From="No-Reply@gmail.com" >
</MailDefinition>
<UserNameTemplate >
<fieldset class="passwordRecovery">
<legend>Password Recovery</legend>
<p>
<asp:Label ID="UserNameLabel" runat="server" AssociatedControlID="UserName">Username:</asp:Label>
<asp:TextBox ID="UserName" runat="server" CssClass="textEntry"></asp:TextBox>
<asp:RequiredFieldValidator ID="UserNameRequired" runat="server" ControlToValidate="UserName"
CssClass="failureNotification" ErrorMessage="User Name is required." ToolTip="User Name is required."
ValidationGroup="PasswordRecovery1">*</asp:RequiredFieldValidator>
</p>
<p>
<asp:ValidationSummary ID="LoginUserValidationSummary" runat="server"
CssClass="failureNotification" Height="37px"
ValidationGroup="PasswordRecovery1" Width="378px" />
<p>
<br />
</p>
</p>
</fieldset>
<p align="right">
<asp:Button ID="Submit" runat="server" CommandName="Submit" Text="Submit"
ValidationGroup="passwordRecovery" />
</p>
</UserNameTemplate>
<QuestionTemplate>
<table cellpadding="1" cellspacing="0" style="border-collapse:collapse;">
<tr>
<td>
<table cellpadding="0" style="height:104px;width:409px;">
<tr>
<td align="left" colspan="2">
Identity Confirmation</td>
</tr>
<tr>
<td align="left" colspan="2">
Answer the following question to receive your password.</td>
</tr>
<tr>
<td align="right">
User Name:</td>
<td>
<asp:Literal ID="UserName" runat="server"></asp:Literal>
</td>
</tr>
<tr>
<td align="right">
Question:</td>
<td>
<asp:Literal ID="Question" runat="server"></asp:Literal>
</td>
</tr>
<tr>
<td align="right">
<asp:Label ID="AnswerLabel" runat="server" AssociatedControlID="Answer">Answer:</asp:Label>
</td>
<td>
<asp:TextBox ID="Answer" runat="server"></asp:TextBox>
<asp:RequiredFieldValidator ID="AnswerRequired" runat="server"
ControlToValidate="Answer" ErrorMessage="Answer is required."
ToolTip="Answer is required." ValidationGroup="PasswordRecovery1"
CssClass="failureNotification">*</asp:RequiredFieldValidator>
</td>
</tr>
<tr>
<td align="left" colspan="2" style="color:Red;">
<asp:Literal ID="FailureText" runat="server" EnableViewState="False"></asp:Literal>
<br />
<asp:ValidationSummary ID="LoginUserValidationSummary" runat="server"
CssClass="failureNotification" Height="37px"
ValidationGroup="PasswordRecovery1" Width="378px" />
</td>
</tr>
<tr>
<td align="right" colspan="2">
<asp:Button ID="SubmitButton" runat="server" CommandName="Submit" Text="Submit"
ValidationGroup="PasswordRecovery1" onclick="SubmitButton_Click" />
</td>
</tr>
</table>
</td>
</tr>
</table>
</QuestionTemplate>
<SuccessTemplate>
<table cellpadding="1" cellspacing="0" style="border-collapse:collapse;">
<tr>
<td>
<table cellpadding="0" style="height:104px;width:409px;">
<tr>
<td>
Your password has been sent to you.</td>
</tr>
</table>
</td>
</tr>
</table>
</SuccessTemplate>
</asp:PasswordRecovery>
</asp:Content>
IF the user enters the correct security question answer, the user sees the success page and an email is properly dispatched to the user.
I understand people have had multithreading problems with SQLite and usually this is the error that is produced, but This provider is meant to mimmick the SQLprovider that microsoft made but for SQLite. What I mean by this is, the .net Controls that microsoft have to offer, "PasswordRecovery" should not have queries that cause concurrency issues.
UPDATE: The c# code is embedded in one of the classes that this .NET control uses and is executed behind the scenes, so I can't pinpoint what actual method makes the database lock out. I am guessing its MembershipUser.ResetPassword(passwordAnswer). I implemented an onClick method after answering the security question that tries to manually reset the password, used that method, and thats where it locked the database.I also tried SQLiteMembershipProvider.ResetPassword(), and that also locked the database. Keep in mind, I am not explicitly making database queries, the .net providers are. So I can be sure that the queries are disposed of properly.
I appreciate your input as I have looked for similar problems with this control and have found nothing.
Thanks.