How to force user to enter value in a database field - c#

With my Application, i want to make sure if user enter no value in a textbox, and click on the
save button to send data in the sqlserver db.The database side validation prevent this violation and set ErrorMessage which my application will catch and Display a meaninful Message
to the User. For each required field i set it to NOT NULL. But when i test,i can still enter
enter empty textbox values it gets inserted with out value.
what am i missing?
string connectionstring = "Data Source=abcdef;Initial Catalog=HMS;Persist Security Info=True;User ID=sysad;Password=abcdef";
SqlConnection connection = new SqlConnection(connectionstring);
string SelectStatement = "SELECT * FROM tablename where RegistrationNo = #RegistrationNo";
SqlCommand insertcommand = new SqlCommand(SelectStatement, connection);
insertcommand.Parameters.AddWithValue("#RegistrationNo", textBox10.Text);
SqlDataReader reader;
try
{
connection.Open();
reader = insertcommand.ExecuteReader();
while (reader.Read())
{
textBox11.Text = reader["RegistrationNo"].ToString();
textBox1.Text = reader["Appearance"].ToString();
textBox2.Text = reader["VolumePH"].ToString();
textBox3.Text = reader["Mobility"].ToString();
textBox4.Text = reader["Viability"].ToString();
textBox5.Text = reader["Head"].ToString();
textBox6.Text = reader["MiddlePiece"].ToString();
textBox7.Text = reader["Tail"].ToString();
textBox8.Text = reader["SpermCount"].ToString();
dateTimePicker1.Text = reader["Date"].ToString();
textBox9.Text = reader["Comment"].ToString();
}//end while
reader.Close();
}
catch (Exception ex)
{
throw ex;
}//end catch

What am i missing?
I think you are missing a distinction between null and an empty string.
Databases distinguish between null and empty. If your ToString succeeds, then you have a non-null string there, and so DB is happy to accept it as a valid value.
In general, using DB for user-side validation is somewhat wasteful: if you know that the field must not be empty, you should check for it in the UI; DB validation should serve as the last resort that preserves the integrity of your data model.

You can use requiredfield validator in the Server side code and validate. If it is empty string return error there itself.
Going to sql server and throwing error is bad.
if(txtBox.Text.Trim() == string.Empty)
//throw "cannot be null error to user;

Its better to check the user input in the user interface. If the user have to enter some value you should check it before trying to insert it to the database.

May be a good idea is to check the fields when you press the button. Example:
private void button_Click(object sender, EventArg e){
if (textbox1.Text == ""){
MessageBox.Show("Your message to the user");
}
}
Hope it helps

Related

ADODB.Command, finding rows that satisfy a condition

The work is done in C#, with an Access database to connect to.
Currently, I want to retrieve the number of accounts from the table ACCOUNT_T that satisfy the user's inputted credentials (username, email, password).
The table has 3 attributes: acc_username VARCHAR(30), acc_email VARCHAR(50), and acc_password VARCHAR(30)
The table has only one entree: 'Tester', 'test#mail.com', 'TestPass'
I want to check number of rows/entrees in the database that match the user's inputted credentials (every account is unique, so assume no duplicates), and used the code shown below.
//Checks whether the user has entered the correct credentials
//If correct info is entered, redirect user to the Main Menu page
private void Login_Login_Button_Click(object sender, EventArgs e)
{
//Open connection
ADODB.Connection connection = new ADODB.Connection();
connection.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=main_db;Jet OLEDB:Engine Type=5;";
connection.Open();
//Create command and object
ADODB.Command command = new ADODB.Command();
object rowsAffected;
//Setting up command and parameters
command.ActiveConnection = connection;
command.CommandText = "SELECT COUNT(*) FROM ACCOUNT_T WHERE acc_username = \'#USERNAME\' AND acc_email = \'#EMAIL\' AND acc_password = \'#PASSWORD\'";
command.Parameters.Append(command.CreateParameter("#USERNAME", DataTypeEnum.adVarChar, ParameterDirectionEnum.adParamInput, 200, Login_Username_TextBox.Text));
command.Parameters.Append(command.CreateParameter("#EMAIL", DataTypeEnum.adVarChar, ParameterDirectionEnum.adParamInput, 200, Login_Email_TextBox.Text));
command.Parameters.Append(command.CreateParameter("#PASSWORD", DataTypeEnum.adVarChar, ParameterDirectionEnum.adParamInput, 200, Login_Password_TextBox.Text));
//Execute command and store into RecordSet
ADODB.Recordset recordSet = command.Execute(out rowsAffected);
//Output A
MessageBox.Show(recordSet.RecordCount.ToString());
//Output B
MessageBox.Show(((int)rowsAffected).ToString());
connection.Close();
if ((int)rowsAffected == 1)
{
MainMenu_User_Label.Text = "Logged In As: " + Login_Username_TextBox.Text;
SetupPanel(MainMenu_Panel);
}
else
{
MessageBox.Show("Wrong Credentials.", "Login Failed", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
However, as marked above, Output A gives the value -1 for "recordSet.RecordCount.ToString()" and Output B gives 0 for "((int)rowsAffected).ToString()". The output is the same regardless of what the user input is, right or wrong. (Meaning that the same output is given whether the user's inputted data is already in the database or not)
Is there something wrong with the code?
private string ConnectionString {get { return "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=main_db;Jet OLEDB:Engine Type=5;"; } };
private void Login_Login_Button_Click(object sender, EventArgs e)
{
string sql = "
SELECT COUNT(*)
FROM ACCOUNT_T
WHERE acc_username = ? AND acc_email = ? AND acc_password = ?";
int rowsAffected = 0;
using (var connection = new OleDbConnection(ConnectionString))
using (var command = new OleDbCommand(sql, connection))
{
// Use OleDbType enum values to match database column types and lengths.
// I have to guess, but you can get exact values from your database.
// Also, OleDb uses positional parameters, rather than names.
// You have to add the parameters in the order they appear in the query string.
command.Parameters.Add("acc_username", OleDbType.VarChar, 200).Value = Login_Username_TextBox.Text;
command.Parameters.Add("acc_email", OleDbType.VarChar, 200).Value = Login_Email_TextBox.Text;
command.Parameters.Add("acc_password", OleDbType.VarChar, 200).Value = Login_Password_TextBox.Text;
cn.Open();
rowsAffected = (int)command.ExecuteScalar();
} //leaving the using block will guarantee the connection is closed, even if an exception is thrown
MessageBox.Show(rowsAffected.ToString());
if (rowsAffected == 1)
{
MainMenu_User_Label.Text = "Logged In As: " + Login_Username_TextBox.Text;
SetupPanel(MainMenu_Panel);
}
else
{
MessageBox.Show("Wrong Credentials.", "Login Failed", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
While I'm here, I also need to mention this is exceptionally poor password handling. It is NEVER okay to store a password like this, even for a simple personal or testing app. This is one of those things that's too important to do wrong, even in learning code. The problem is people tend to re-use passwords, so a breach for your simple testing app might also provide an attacker with credentials which grant access to something far more important. Just don't do it.
Instead, you must create a unique salt (or nonce) value for each user. When a user sets the password, you prepend the salt to the new password. Then you create a cryptographic hash of the combined value using an algorithm like BCrypt and prepend the salt to final value again. Now you only store this altered information. Never store the actual password. When someone tries to login, you retrieve the stored information, extract the salt, and use the same procedure on the attempted password. Now you can compare the hash values rather than the raw passwords.

Specific if else statement after using a SQL call to retrieve data from a database. Visual Studios 2015 / C#

I'm attempting to add the finishing touch to a project I've been working on and am currently trying to modify a feature that I've created. The feature being that if a student has completed an examination, they are able to view the results. However, what I want to do is create an if else statement that is essentially: if the exam has been taken and completed, then they are redirected to the page that shows them the specific exam's results. Else, it returns a message at the top of the page stating "This examination has not been completed yet."
The current code I have (which is operated through a button on the page) is:
protected void btnViewPrevExam_Click(object sender, EventArgs e)
{
Session["intExaminationID"] = ddlExamination.SelectedValue;
Int32 int32StudentID = Convert.ToInt32(Session["StudentID"]);
Session["int32StudentID"] = Convert.ToInt32(int32StudentID);
// Define the ADO.NET connection object.
SqlConnection objSqlConnection = new SqlConnection(WebConfigurationManager.ConnectionStrings["OPT"].ConnectionString);
// Develop the SQL call.
// Develop the SQL call.
String strSQL = "";
strSQL = "SELECT AnswerID, Question, OptionA, OptionB, OptionC, OptionD, CorrectAnswer, Answer ";
strSQL += " FROM Question, Answer, Examination, Student ";
strSQL += " WHERE Examination.ExaminationID = " + ddlExamination.SelectedValue;
strSQL += " AND Student.StudentID = " + int32StudentID;
strSQL += " AND Answer.QuestionID = Question.QuestionID ";
strSQL += " AND Answer.StudentID = Student.StudentID ";
strSQL += " AND Examination.ExaminationID = Question.ExaminationID ";
// Create the SQL command object.
SqlCommand objSqlCommand = new SqlCommand(strSQL, objSqlConnection);
// Retrieve the row from the table.
objSqlConnection.Open();
SqlDataReader objSqlDataReader = objSqlCommand.ExecuteReader();
objSqlDataReader.Read();
if (strSQL != null)
{
objSqlDataReader.Close();
objSqlConnection.Close();
Response.Redirect("StudentExamResults.aspx");
}
else
{
this.Master.MessageForeColor = System.Drawing.Color.Red;
this.Master.Message = "The selected examination has not been completed.";
}
}
What this button does currently is that it will send the student to the examination results page regardless if the examination has been completed or not. This is due to the line "if (strSQL != null)" and it never being null because the SQL call has been made and filled. I've attempted other ideas, as well as performing a objSqlDataReader for the AnswerID but it didn't work properly. This is a small extra feature I'd like to add to this project that I thought of and would be very pleased if I could find some help on sorting out what I'm doing wrong. Thank you in advance!
Testing if strSQL is not null will always succeed because you are setting it to a non-null value earlier in the method.
To see if a record already exists for a previously-completed examination, you need to check the return value of the call to objSqlDataReader.Read(); it will return true as long as there are additional rows (or, in this case, a first row) to consume from your SELECT query. Thus, change this...
objSqlDataReader.Read();
if (strSQL != null)
{
...to this...
if (objSqlDataReader.Read())
{
As an additional note, consider wrapping objSqlConnection, objSqlCommand, and objSqlDataReader in using blocks to ensure they are properly closed/disposed. As it is now, you are not closing objSqlDataReader and objSqlConnection when the exam needs to be completed, and objSqlCommand is not disposed at all. objSqlDataReader would then be closed as follows, regardless of which branch of the if is taken...
using (SqlDataReader objSqlDataReader = objSqlCommand.ExecuteReader())
{
if (objSqlDataReader.Read())
{
//objSqlDataReader.Close();// No longer necessary - handled by using
objSqlConnection.Close();
Response.Redirect("StudentExamResults.aspx");
}
else
{
this.Master.MessageForeColor = System.Drawing.Color.Red;
this.Master.Message = "The selected examination has not been completed.";
}
}
Assuming you don't care about the contents, rather you just want to check if the row exists, you can do something like this:
string sql = "SELECT COUNT(AnswerID) FROM Question ........ WHERE ......";
using (var connection = CreateConnection()) {
using (var cmd = new SqlCommand(sql, connection)) {
bool exists = (int) cmd.ExecuteScalar() > 0;
if (exists) {
Response.Redirect("StudentExamResults.aspx");
} else {
// Do the other thing
}
}
}

Login Button to read sql database and confirm values entered into textboxes with values in said sql database throws error

I am a noob attempting to create a login page where the user enters their username and password that is already in the sqldatabase connected to the textboxes/button/form. The below code is my best attempt at doing so, but upon debugging it throws catch despite the textbox values entered being registered in the sql database. If any additional information is needed please ask.
private bool compareStoD(string teststring1, string teststring2)
{
return String.Compare(teststring1, teststring2, true, System.Globalization.CultureInfo.InvariantCulture) == 0 ? true : false;
}
private void button1_Click_1(object sender, EventArgs e)
{
try
{
SqlConnection connection = new SqlConnection(#"Data Source=DESKTOP-P3JSE1C;Initial Catalog=logins;Integrated Security=True");
connection.Open();
SqlCommand checker = new SqlCommand("SELECT COUNT (*) from users WHERE username='" + textBox1.Text + "'AND pssword='" + textBox3.Text + "'", connection);
SqlDataReader reader = checker.ExecuteReader();
string usernameText = textBox1.Text;
string psswordText = textBox3.Text;
while (reader.Read())
{
if (this.compareStoD(reader["username"].ToString(), textBox1.Text) && // replace textbox1.Text with text string usernameText
this.compareStoD(reader["pssword"].ToString(), textBox3.Text)) //replace textbox3.Text with text string psswordText
{
main wen = new main();
wen.Show();
}
}
reader.Close();
connection.Close();
}
catch
{
MessageBox.Show("Incorrect password or username.");
}
}
It is most likely throwing an exception because your query is asking for the count but then you are reading columns username and password which do not exist in the reader. This is your query:
SELECT COUNT (*)
Change that to this:
SELECT username, password ...
Also, unless you want every savvy user to access your application, use SqlParameter to avoid SQL Injection
Another Suggestion
I am not sure what main is, my assumption it is some window, but I would not show it where you are showing right now. Try to close the reader as soon as possible and then show the window if the user is authenticated like this.
bool userIsAuthenticated = false;
if (reader.Read())
{
// if a row was returned, it must be the row for the user you queried
userIsAuthenticated = true;
}
reader.Close();
connection.Close();
// Now that the reader is closed, you can show the window so the reader does not stay
// open during the duration of the main window
if (userIsAuthenticated)
{
main wen = new main();
wen.Show();
}
Select count returns the count not the row, if you want the row itself change to select username, password instead of select count(*) . See this link
There is over work being done by your code. You are querying the database by comparing the username and password values from UI to the values in the table. And once and if values are retrieved from the database you are again comparing value from UI to the values coming from the database. This is unnecessary.
The query will return the values only if values match in the database so you don't need to compare them again. So method compareStoD is not required at all.
The button1_Click can be changed as following to make it simpler.
private void button1_Click_1(object sender, EventArgs e)
{
try
{
SqlConnection connection = new SqlConnection(#"Data Source=DESKTOP-P3JSE1C;Initial Catalog=logins;Integrated Security=True");
connection.Open();
SqlCommand checker = new SqlCommand("SELECT COUNT (*) from users WHERE username=#userName AND pssword = #password", connection);
checker.Parameters.Add(new SqlParameter("#userName", textBox1.Text));
checker.Parameters.Add(new SqlParameter("#password", textBox3.Text));
var count = Convert.ToInt32(checker.ExecuteScalar());
connection.Close();
if(count > 0)
{
main wen = new main();
wen.Show();
}
else
{
MessageBox.Show("Incorrect password or username.");
}
}
catch
{
MessageBox.Show("Incorrect password or username.");
}
}
Also one good practice while supplying values from Textbox, you should use Textbox.Text.Trim() which helps in eliminating the spaces at the beginning and end. These spaces can create a problem in later stage.

Trying to create a log in page

I am trying to create a login page where you would enter in a username and a password. It will query the database for the information you typed in, and if it is in the database, it will log me into the program. If not, it will display a message saying information is not correct.
Here is what I have so far.
private void okButton_Click(object sender, RoutedEventArgs e)
{
try
{
SqlConnection UGIcon = new SqlConnection();
UGIcon.ConnectionString = "XXXXXXXXX; Database=XXXXXXXX; User Id=XXXXXXX; password=XXXXXXXXX";
UGIcon.Open();
SqlCommand cmd = new SqlCommand("SELECT User(Username, '') AS Username, User(Password,'') AS Password, FROM User WHERE Username='"
+ txtUsername.Text + "' and Password='" + txtPassword.Password + "'", UGIcon);
SqlDataReader dr = cmd.ExecuteReader();
string userText = txtUsername.Text;
string passText = txtPassword.Password;
while (dr.Read())
{
if (this.userText(dr["stUsername"].ToString(), userText) &&
this.passText(dr["stPassword"].ToString(), passText))
{
MessageBox.Show("OK");
}
else
{
MessageBox.Show("Error");
}
}
dr.Close();
UGIcon.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
But, the only problem is it does not work at all. I am not sure I have the correct statements to query the database either. I am also getting an error on the "this.userText" As well.
{
if (this.userText(dr["stUsername"].ToString(), userText) &&
this.passText(dr["stPassword"].ToString(), passText))
{
For the error I'm getting, it tells me the WPF does not contain a definition for it
I am a little unsure of how to fix it and go about it as this is the first time I've had to do this. But I think I have a decent start to it though.
There are a couple of things wrong with this structure:
this.userText(dr["stUsername"].ToString(), userText)
First, userText isn't a function, it's a local variable. So I'm not sure what you're even trying to do by invoking it as a function. Are you just trying to compare the variable? Something like this?:
this.userText.Equals(dr["stUsername"].ToString())
Second, the error is telling you that the object doesn't contain a definition for userText because, well, it doesn't. When you do this:
this.userText
you're specifically looking for a class-level member called userText on the object itself. But your variable is local to the function:
string userText = txtUsername.Text;
So just drop the this reference:
userText.Equals(dr["stUsername"].ToString())
Third, the column reference is incorrect. Note how you define the columns in your SQL query:
SELECT User(Username, '') AS Username, User(Password,'') AS Password ...
The column is called Username, not stUsername:
userText.Equals(dr["Username"].ToString())
Edit: #Blam made a good point in a comment, which demonstrates a logical error in the code. If no results are returned from your query, the while loop will never execute. So no message will be shown. You can check for results with something like HasRows:
if (dr.HasRows)
MessageBox.Show("OK");
else
MessageBox.Show("Error");
This kind of renders the previous things moot, of course. But it's still good to know what the problems were and how to correct them, so I'll leave the answer whole for the sake of completeness regarding the overall question.
A few other notes which are important but not immediately related to your question...
Your code is vulnerable to SQL injection attacks. You'll want to look into using parameterized queries instead of concatenating string values like that. Essentially what this code does is treat user input as executable code on the database, allowing users to write their own code for your application.
Please don't store user passwords in plain text. The importance of this can not be overstated. The original text of a password should never be readable from storage. Instead, store a hash of the password. There's a lot more to read on the subject.
Look into using blocks to dispose of resources when you're done with them.
SqlCommand cmd = new SqlCommand("SELECT count(*) FROM User WHERE Username='"
+ txtUsername.Text + "' and Password='" + txtPassword.Password + "'", UGIcon);
Int32 rowsRet = (Int32)cmd.ExecuteScalar();
if(rowsRet > 0)
{
MessageBox.Show("OK");
}
else
{
MessageBox.Show("Error");
}
You still have exposure to SQL injection attack.

C# - MySQL Database, Check password is correct when it's encrypted?

I'm using a MySQL database with my program and when I check if the password a user enters is correct, it always says it's invalid. I do the same with email but it works. I think it's because my PHP script encrypts the password when it's created on the page. Here is my code:
try
{
string command = "SELECT email FROM uc_users WHERE email = '#email';";
string command2 = "SELECT password FROM uc_users WHERE password = '#password';";
// CONNECTION DETAILS
connection = new MySqlConnection(connectionString);
connection.Open();
// COMMAND DETAILS
MySqlCommand email = new MySqlCommand(command, connection);
MySqlCommand passwordc = new MySqlCommand(command2, connection);
// PARAMETERS
email.Parameters.AddWithValue("#email", txtEmail.Text);
passwordc.Parameters.AddWithValue("#password", txtPassword.Text);
// READER DETAILS
MySqlDataReader dr;
MySqlDataReader dr2;
// CHECK DETAILS
dr = email.ExecuteReader();
string tempE = dr.Read() ? dr.GetString(0) : "Invalid Email";
dr.Close();
dr2 = passwordc.ExecuteReader();
string tempP = dr2.Read() ? dr.GetString(0) : "Invalid Password";
dr2.Close();
MessageBox.Show(tempE + " " + tempP);
if (tempE == txtEmail.Text && tempP == txtPassword.Text)
{
connection.Close();
tempE = "";
tempP = "";
string email2 = txtEmail.Text;
frmAppHub frm = new frmAppHub(email2);
frm.Show();
this.Hide();
}
else
{
MessageBox.Show("Invalid login details. Please try again.");
connection.Close();
tempE = "";
tempP = "";
}
}
catch(MySqlException ex)
{
MessageBox.Show("MySQL Error - Code AHx004: " +ex.Message);
connection.Close();
}
Any ideas how to do it? Any help is appreciated.
The query is fundamentally broken. There should be one query and the approach should be like:
// No quotes around placeholder and only ONE query that does NOT select on the password.
// If the query selects on the password then it means that the password is either
// stored as plaitext (which is not good) or the database value can be computed without
// per-user information (which is also not good).
string command = "SELECT email, password FROM uc_users WHERE email = #email";
// Only look based on the user (the email column should have a Unique constraint)
// as (although a unique salt makes it very unlikely) passwords are not unique
// nor do they identify users.
email.Parameters.AddWithValue("#email", txtEmail.Text);
// Then read the password for the given user
dr = email.ExecuteReader();
if (!dr.Read()) {
// User not found - just stop.
return false;
}
string dbPassword = dr.GetString(1);
return IsPasswordMatch(dbPassword, txtPassword.Text);
Now, IsPasswordMatch is a function which, when, given the database password and the plain-text password, should determine if they are a match by applying all the appropriate hash/salt/whatever transforms (see my first comment). In any case, all the logic can be safely tucked away in there.
It might look something like:
bool IsPasswordMatch (string dbPassword, string plaintext) {
var salt = GetSalt(dbPassword);
var dbHash = GetHash(dbPassword);
var ptHash = Hash(salt + plaintext);
return dbHash == ptHash;
}
I've left in the methods as "high level operations" that need to be adapted to whatever was used in the first place; however, the basic operation should now be apparent. Just repeat the same process as was used to create the database value in the first place - is it the same in both cases?
Make sure to read up on using as well - this will enable resources to be cleaned up easily without fuss.
Yes, you need to generate hash of your password, that must be identical stored in database and than query your command with this hash, instead of plain text password.
If you have different strings in DB and #passowrd - you always have invalid result.

Categories

Resources