How to call a method from another class inside a Window? - c#

So I have a Class called "User" in which I have the following method and code:
public void Login()
{
LoginWindow l = new LoginWindow();
if (l.tbxEmail.Text != "" && l.tbxPassword.Text != "")
{
string query = "SELECT * FROM UsersTBL";
l.con.Open();
l.com = l.con.CreateCommand();
l.com.CommandText = query;
SqlDataReader dr = l.com.ExecuteReader();
if (dr.Read())
{
if (dr["Email"].Equals(l.tbxEmail.Text.ToString()) && dr["UserPassword"].Equals(l.tbxPassword.Text.ToString()))
{
AppWindow a = new AppWindow();
a.Show();
}
else
l.lblMissingParameter.Content = "Incorrect Password or Email entered";
}
}
}
And in my LoginWindow I have:
public partial class LoginWindow:Window
{
User u = new User();
private void BtnSignup_Click(object sender, RoutedEventArgs e)
{
u.Login();
}
}
When I try to call my Login method via class instantiation nothing works, why is that? Am I calling it the wrong way?

This should work, although I left comments on things that should be addressed.
User class:
public bool Login(SqlConnection con, string email, string password)
{
const string query = "SELECT 1 FROM UsersTBL WHERE Email = #email AND UserPassword = #password";
if (!string.IsNullOrWhiteSpace(email) && !string.IsNullOrWhiteSpace(password))
{
try
{
con.Open();
var cmd = con.CreateCommand();
cmd.CommandText = query;
//Correct SqlDbTypes if necessary
cmd.Parameters.Add("#email", SqlDbType.VarChar);
cmd.Parameters["#email"].Value = email;
cmd.Parameters.Add("#password", SqlDbType.VarChar);
//Should NOT be storing passwords as plain text in the database
cmd.Parameters["#password"].Value = password;
if (cmd.ExecuteScalar() == 1)
return true;
}
catch (Exception e)
{
//log e somehow or eliminate this catch block
}
finally
{
//Close the connection if still open
if (con != null && con.State != ConnectionState.Closed)
con.Close();
}
}
return false;
}
LoginWindow class:
public partial class LoginWindow : Window
{
private void BtnSignup_Click(object sender, RoutedEventArgs e)
{
var u = new User();
if (u.Login(con, tbxEmail.Text, tbxPassword.Text))
{
AppWindow a = new AppWindow();
a.Show();
}
else
lblMissingParameter.Content = "Incorrect Password or Email entered";
}
}

To clarify, you had this problem because the tbxEmail and tbxPassword variables in your User class where not the same as the ones in your main class.
You should create both variable at class scope:
public class User {
TextBox tbxEmail; // could be strings
PasswordBox tbxPassword;
public User (TextBox tbxEmail, TextBox tbxPassword) {
this.tbxEmail = tbxEmail;
this.tbxPassword = tbxPassword;
}
}
And then:
User user = new User(tbxEmail,tbxPassword);
user.Login();
Or, create a static method (static method can't use global variables, so everything you need have to be passed as parameter of the method or created inside of it).:
public static void Login (string email, string password){
// code here
}

I wrote a rudimentary login page for one of my school projects similar to this:
private void signInButton_Click(object sender, EventArgs e)
{
DataProcedures data = new DataProcedures();
User userInfo = new User(usernameTextbox.Text, passwordTextbox.Text);
userInfo.userId = data.verifyUser(userInfo);
if (userInfo.userId != -1)
{
AppWindow a = new AppWindow();
a.Show();
}
else
{
errorLabel.Show();
}
}
public int verifyUser(User userInfo)
{
MySqlConnection conn = new MySqlConnection(connectionString);
int userId = -1;
string returnedUserName;
string returnedPassword;
try
{
conn.Open();
MySqlCommand checkUserNameCmd = conn.CreateCommand();
checkUserNameCmd.CommandText = "SELECT EXISTS(SELECT userName FROM user WHERE userName = #username)";
checkUserNameCmd.Parameters.AddWithValue("#username", userInfo.username);
returnedUserName = checkUserNameCmd.ExecuteScalar().ToString();
MySqlCommand checkPasswordCmd = conn.CreateCommand();
checkPasswordCmd.CommandText = "SELECT EXISTS(SELECT password FROM user WHERE BINARY password = #password AND userName = #username)";//"BINARY" is used for case sensitivity in SQL queries
checkPasswordCmd.Parameters.AddWithValue("#password", userInfo.password);
checkPasswordCmd.Parameters.AddWithValue("#username", userInfo.username);
returnedPassword = checkPasswordCmd.ExecuteScalar().ToString();
if (returnedUserName == "1" && returnedPassword == "1")
{
MySqlCommand returnUserIdCmd = conn.CreateCommand();
returnUserIdCmd.CommandText = "SELECT userId FROM user WHERE BINARY password = #password AND userName = #username";
returnUserIdCmd.Parameters.AddWithValue("#password", userInfo.password);
returnUserIdCmd.Parameters.AddWithValue("#username", userInfo.username);
userId = (int)returnUserIdCmd.ExecuteScalar();
}
}
catch (Exception ex)
{
Console.WriteLine("Exception thrown verifying user: " + ex);
}
finally
{
conn.Close();
}
return userId;
}
Hope this helps.

Related

How to assign database values into global variables in a class

I am creating a windows forms in C# with following code:
private void button1_Click(object sender, EventArgs e)
{
using (OleDbConnection connection = new OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\\Users\\RAV21001310\\OneDrive\\Database1.accdb;"))
{
connection.Open();
using (OleDbCommand command = new OleDbCommand("SELECT * FROM tblUser WHERE Username=#Username AND Password=#Password", connection))
{
command.Parameters.AddWithValue("#Username", username.Text);
command.Parameters.AddWithValue("#Password", password.Text);
using (OleDbDataReader reader = command.ExecuteReader())
{
int count = 0;
while (reader.Read())
{
count = count + 1;
}
if (count == 1)
{
MessageBox.Show("Username and password is correct");
}
if (count > 1)
{
MessageBox.Show("Duplicate username and password");
}
if (count == 0)
{
MessageBox.Show("Username or password incorrect");
}
}
}
connection.Close();
}
}
}
public class User
{
public string Username;
public string Password;
public string FirstName;
public string LastName;
public string Gender;
public int Age;
public int TotalPoints;
}
The first part is for a login form. The part I am struggling with is how to make it so when a user logs in it gets all his relevant information like username, password, TotalPoints etc from the database in ms access and assigns it to the variables in the class so they can be called throughout different forms.
// Add parameters for the username and password
command.Parameters.Add("#Username").Value = username.text;
command.Parameters.Add("#Password").Value = password.text;
// Execute the query
SqlDataReader reader = command.ExecuteReader();
// Validate the user's credentials
bool isValid = false;
if (reader.Read())
{
User user = new User
{
Id = Convert.ToInt32(reader["Id"]),
// Add all fileds you want to use...
}
isValid = true;
}
// Close the reader and the connection
reader.Close();
connection.Close();
// Do someting with result
...

LOGIN FORM using class

I have button1_click in Form1.. and i want string usernamebox.text and passwordbox.text to login class i created it before.. and I got stuck here.. Login class work but button code not how Get textBox value in from1 to login class
private void button1_Click(object sender, EventArgs e)
{
String c1 = new String();
c1 = UsernameBox.Text;
Login.Validation_user(c1.ToString());
String c2 = new String();
c2 = PasswordBox.Text;
Login.Validation_pass(c2.ToString());
}
this my string
static string connectionClient = ConfigurationManager.ConnectionStrings["Sample2"].ConnectionString;
private static bool Validation { get; set; }
this my login class "Password"
public static void Validation_pass (string password)
{
if (!Validation)
{
using (MySqlConnection sqlConn = new MySqlConnection(connectionClient))
{
string checkForLogQuery = "SELECT * FROM Login WHERE Password=#pass";
MySqlCommand cmd = new MySqlCommand(checkForLogQuery, sqlConn);
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#pass", password);
sqlConn.Open();
int result = Convert.ToInt32(cmd.ExecuteScalar());
if (result == 1)
{
MessageBox.Show("wellcome");
Form9 f9 = new Form9();
f9.Close();
f9.DialogResult = System.Windows.Forms.DialogResult.Cancel;
Form10 f10 = new Form10();
f10.Show();
Validation = true;
}
else
{
MessageBox.Show("Your Key was incorrect");
Validation = false;
}
}
}
else
{
}
}
and "username" it same what is above
You need to validate both password and username together
Here is an example
private void button1_Click(object sender, EventArgs e) {
var res = Login.Validate(PasswordBox.Text, UsernameBox.Text);
if (res)
MessageBox.Show("wellcome");
else
MessageBox.Show("UserName or Password dose not match.");
}
public static bool Validate(string password, string userName) {
if (string.IsNullOrWhiteSpace(password) || string.IsNullOrWhiteSpace(userName))
return false;
using(MySqlConnection sqlConn = new MySqlConnection(connectionClient)) {
string checkForLogQuery = "SELECT 1 FROM Login WHERE Password=#pass and LOWER(UserName) = LOWER(#userName)";
MySqlCommand cmd = new MySqlCommand(checkForLogQuery, sqlConn);
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#pass", password);
cmd.Parameters.AddWithValue("#userName", userName);
sqlConn.Open();
var value = Convert.ToBoolean(cmd.ExecuteScalar());
sqlConn.Close();
return value;
}
}

C# winforms save UserID value to class

I'm trying to save UserID value to a class after an successful login. When the login button is clicked, it validates the login into in text boxes using the #user, #pass.
My problem is that I do not know how to write a sql reader and save the resulting int to a class. I also do not know how to write that class.
I need to save it to a class so I can use it on different forms to check what account the user is logged into.
EDIT: Updated code from suggestions but i get errors Error picture any ideas where i did a mistake?
private LoginUser validate_login(string user, string pass)
{
db_connection();
MySqlCommand cmd = new MySqlCommand();
cmd.CommandText = "Select * from table2 where username=#user and password=#pass";
cmd.Parameters.AddWithValue("#user", user);
cmd.Parameters.AddWithValue("#pass", pass);
cmd.Connection = connect;
LoginUser usr = new LoginUser();
MySqlDataReader login = cmd.ExecuteReader();
while(login.Read())
{
connect.Close();
usr.UserID = login["UserID"];
usr.valid = true;
}
return usr;
}
private void button1_Click(object sender, EventArgs e)
{
{
string user = usertype.Text;
string pass = password.Text;
if (user == "" || pass == "")
{
MessageBox.Show("Empty Fields Detected ! Please fill up all the fields");
return;
}
bool r = validate_login(user, pass);
if (r)
{
LoginUser usr = new LoginUser();
usr = validate_login(user, pass);
if (usr.valid)
{
Console.WriteLine(String.Format("{0}", usr.UserID));
UserDetails.m_gnUserId = Convert.ToInt32(reader["UserID"]);
}
}
}
}
public partial class Form1 : Form
{
private LoginUser validate_login(string user, string pass)
{
...
LoginUser usr = new LoginUser();
MySqlDataReader login = cmd.ExecuteReader();
while(login.Read())
{
connect.Close();
usr.UserID = login["UserID"];
usr.valid = true;
}
return usr;
}
private void button1_Click(object sender, EventArgs e)
{
...
LoginUser usr = new LoginUser();
usr = validate_login(user, pass);
if (usr.valid)
{
Console.WriteLine(String.Format("{0}", usr.UserID));
}
}
}
public class LoginUser
{
public Bool valid = false;
public String UserID = "";
// You can have more column name up to matching with your table column.
}
One of the method is as follow
Create a class UserDetails with all the data you want to store
public static class UserDetails
{
public static int m_gnUserId {get;set;}
//Add other variables which you want to store and use across different forms
}
To Store Value
UserDetails.m_gnUserId = Convert.ToInt32(reader["UserID"]); .
This storing of Value will go after Console.WriteLine(String.Format("{0}", reader["UserID"])); in your button1_click event.
To get the value on other form
int UserId = UserDetails.m_gnUserId;

C# Login Form Secure/Correct?

I'm making my final project for C# (school) this year and I promised the last time I got help on this site that I would make sure my SQL was secure and I would make my application secure. Could someone look over my Login Screen and tell me if this is a proper and secure way?
I start by opening my main mdiContainer via Program.cs:
private void Form1_Load(object sender, EventArgs e)
{
fL.ShowDialog();
}
Then this login form shows:
string User = txtUser.Text;
string Pw = txtPw.Text;
int Correct = clDatabase.login(User, Pw);
if (Correct == 1)
{
this.Hide();
}
else
{
MessageBox.Show("De gegevens die u heeft ingevult kloppen niet", "Fout!"); //Above means your input is not correct
}
And in clDatabase.login
public static int login(string GebruikersnaamI, string WachtwoordI)
{
int correct = 0;
SqlConnection Conn = new SqlConnection(clStam.Connstr);
Conn.Open();
using (SqlCommand StrQuer = new SqlCommand("SELECT * FROM gebruiker WHERE usernm=#userid AND userpass=#password", Conn))
{
StrQuer.Parameters.AddWithValue("#userid", GebruikersnaamI);
StrQuer.Parameters.AddWithValue("#password", WachtwoordI);
SqlDataReader dr = StrQuer.ExecuteReader();
if (dr.HasRows)
{
correct = 1;
MessageBox.Show("loginSuccess");
}
else
{
correct = 2;
//invalid login
}
}
Conn.Close();
return correct;
}
Dialog for loginsucces is only there for debug purposes atm
Is this secure? Is this the proper way to have a login form?
EDIT Updated code login form:
private void button1_Click(object sender, EventArgs e)
{
ErrorProvider EP = new ErrorProvider();
if (txtUser.Text == string.Empty || txtPw.Text == string.Empty)
{
if (txtUser.Text == string.Empty)
txtUser.BackColor = Color.Red;
if (txtPw.Text == string.Empty)
txtPw.BackColor = Color.Red;
MessageBox.Show("Er moet wel iets ingevuld zijn!", "Fout");
}
else
{
string User = txtUser.Text;
string Pw = txtPw.Text;
Boolean Correct = clDatabase.login(User, Pw);
if (Correct == true)
{
this.Hide();
}
else
{
MessageBox.Show("Deze combinatie van username en password is niet bekend", "Fout!");
}
}
}
clDatabase:
public static Boolean login(string GebruikersnaamI, string WachtwoordI)
{
Boolean correct = false;
using (SqlConnection Conn = new SqlConnection(clStam.Connstr))
{
Conn.Open();
using (SqlCommand StrQuer = new SqlCommand("SELECT * FROM gebruiker WHERE usernm=#userid AND userpass=#password", Conn))
{
StrQuer.Parameters.AddWithValue("#userid", GebruikersnaamI);
StrQuer.Parameters.AddWithValue("#password", WachtwoordI);
using (SqlDataReader dr = StrQuer.ExecuteReader())
{
if (dr.HasRows)
{
correct = true;
}
else
{
correct = false;
//invalid login
}
}
}
Conn.Close();
}
return correct;
}
It is secure as far as SQL Injection is concerned, as you are passing parameters. But, do not store password as plain text, instead store its hashed value.
See: How to securely save username/password (local)?

checking user name or user email already exists

I am working in a simple registration page where the user can't enter the same user name or email, I made a code that prevent the user from entering the username and it worked but when I tried to prevent the user from entring the same username or email it didn't work.
and my question is, "How can I add another condition where the user can't enter email that already exists?"
I tried to do it in this code, but it did't work:
protected void Button_Click(object sender, EventArgs e)
{
SqlConnection con = new SqlConnection( ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString );
SqlCommand cmd1 = new SqlCommand("select 1 from Table where Name =#UserName", con);
SqlCommand cmd2 = new SqlCommand("select 1 from Table where Email=#UserEmail", con);
con.Open();
cmd1.Parameters.AddWithValue("#UserName", Name_id.Text);
cmd2.Parameters.AddWithValue("#UserEmail", Email_id.Text);
using (var dr1 = cmd1.ExecuteReader())
{
if (dr1.HasRows)
{
Label1.Text = "user name already exists";
}
using (var dr2 = cmd2.ExecuteReader())
{
if (dr2.HasRows)
{
Label1.Text = "email already exists";
}
else
{
dr1.Close();
dr2.Close();
//add new users
con.Close();
}
}
}
}
but i get this error:
There is already an open DataReader associated with this Command which must be closed first.
Like I said in my comment your design is bad !
First you should have Data Access Layer. This should be project in big solutions but in your case you can put it like new directory. In this directory you create SqlManager class here is the code:
public class SqlManager
{
public static string ConnectionString
{
get
{
return ConfigurationManager.ConnectionStrings["DevConnString"].ConnectionString;
}
}
public static SqlConnection GetSqlConnection(SqlCommand cmd)
{
if (cmd.Connection == null)
{
SqlConnection conn = new SqlConnection(ConnectionString);
conn.Open();
cmd.Connection = conn;
return conn;
}
return cmd.Connection;
}
public static int ExecuteNonQuery(SqlCommand cmd)
{
SqlConnection conn = GetSqlConnection(cmd);
try
{
return cmd.ExecuteNonQuery();
}
catch
{
throw;
}
finally
{
conn.Close();
}
}
public static object ExecuteScalar(SqlCommand cmd)
{
SqlConnection conn = GetSqlConnection(cmd);
try
{
return cmd.ExecuteScalar();
}
catch
{
throw;
}
finally
{
conn.Close();
}
}
public static DataSet GetDataSet(SqlCommand cmd)
{
return GetDataSet(cmd, "Table");
}
public static DataSet GetDataSet(SqlCommand cmd, string defaultTable)
{
SqlConnection conn = GetSqlConnection(cmd);
try
{
DataSet resultDst = new DataSet();
using (SqlDataAdapter adapter = new SqlDataAdapter(cmd))
{
adapter.Fill(resultDst, defaultTable);
}
return resultDst;
}
catch
{
throw;
}
finally
{
conn.Close();
}
}
public static DataRow GetDataRow(SqlCommand cmd)
{
return GetDataRow(cmd, "Table");
}
public static DataRow GetDataRow(SqlCommand cmd, string defaultTable)
{
SqlConnection conn = GetSqlConnection(cmd);
try
{
DataSet resultDst = new DataSet();
using (SqlDataAdapter adapter = new SqlDataAdapter(cmd))
{
adapter.Fill(resultDst, defaultTable);
}
if (resultDst.Tables.Count > 0 && resultDst.Tables[0].Rows.Count > 0)
{
return resultDst.Tables[0].Rows[0];
}
else
{
return null;
}
}
catch
{
throw;
}
finally
{
conn.Close();
}
}
}
After that you should have Business Object Layer. In bigger solution is project in your case directory. If you are in the page TaxesEdit.aspx, you should add Tax.cs class in the BO(business object).
Example of methods for the class, for your first button:
public DataSet GetTaxesByUserName(string userName)
{
SqlCommand cmd = new SqlCommand(#"
select 1 from Table where Name =#UserName");
cmd.Parameters.AddWithValue("#UserName", userName);
return DA.SqlManager.GetDataSet(cmd);
}
You fetch all the needed data in datasets. After that you make checks like taxesDst.Tables[0].Rows.Count > 0 (or == 0)
For Insert you can have method like this:
public virtual void Insert(params object[] colValues)
{
if (colValues == null || colValues.Length % 2 != 0)
throw new ArgumentException("Invalid column values passed in. Expects pairs (ColumnName, ColumnValue).");
SqlCommand cmd = new SqlCommand("INSERT INTO " + TableName + " ( {0} ) VALUES ( {1} )");
string insertCols = string.Empty;
string insertParams = string.Empty;
for (int i = 0; i < colValues.Length; i += 2)
{
string separator = ", ";
if (i == colValues.Length - 2)
separator = "";
string param = "#P" + i;
insertCols += colValues[i] + separator;
insertParams += param + separator;
cmd.Parameters.AddWithValue(param, colValues[i + 1]);
}
cmd.CommandText = string.Format(cmd.CommandText, insertCols, insertParams);
DA.SqlManager.ExecuteNonQuery(cmd);
}
For this you need to have property TableName in the current BO class.
In this case this methods can be used everywhere and you need only one line of code to invoke them and no problems like yours will happen.
You have opened another DataReader inside the First and thats causing the problem. Here I have re-arranged your code a bit
SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString);
SqlCommand cmd1 = new SqlCommand("select 1 from Table where Name =#UserName", con),
cmd2 = new SqlCommand("select 1 from Table where Email=#UserEmail", con);
con.Open();
cmd1.Parameters.AddWithValue("#UserName", Name_id.Text);
cmd2.Parameters.AddWithValue("#UserEmail", Email_id.Text);
bool userExists = false, mailExists = false;
using (var dr1 = cmd1.ExecuteReader())
if (userExists = dr1.HasRows) Label1.Text = "user name already exists";
using (var dr2 = cmd2.ExecuteReader())
if (mailExists = dr2.HasRows) Label1.Text = "email already exists";
if (!(userExists || mailExists)) {
// can add User
}
You need to close one datareader before opening the other one. Although it's not how I'd do it, but you can deal with the runtime error by closing the datareader after each IF:
using (var dr1 = cmd1.ExecuteReader())
{
if (dr1.HasRows)
{
string Text = "user name already exists";
}
dr1.Close();
}
using (var dr2 = cmd2.ExecuteReader())
{
if (dr2.HasRows)
{
string ext = "email already exists";
}
else
{
//add new users
}
dr2.Close();
}
con.Close();
This may work, although there are a few things I would do differently...
protected void Button_Click(object sender, EventArgs e)
{
bool inputIsValid = true;
var con = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString);
var userNameCmd = new SqlCommand("SELECT 1 FROM Table WHERE Name = #UserName", con);
var emailCmd = new SqlCommand("SELECT 1 FROM Table WHERE Email = #UserEmail", con);
con.Open();
userNameCmd.Parameters.AddWithValue("#UserName", Name_id.Text);
emailCmd.Parameters.AddWithValue("#UserEmail", Email_id.Text);
using (var userNameReader = userNameCmd.ExecuteReader())
{
if (userNameReader.HasRows)
{
inputIsValid = false;
Label1.Text = "User name already exists";
}
}
using (var emailReader = emailCmd.ExecuteReader())
{
if (emailReader.HasRows)
{
inputIsValid = false;
Label1.Text = "Email address already exists";
}
}
if (inputIsValid)
{
// Insert code goes here
}
con.Close();
}
Why don't you do something like this:
[Flags]
public enum ValidationStatus
{
Valid = 0 ,
UserNameInUse = 1 ,
EmailInUse = 2 ,
}
public ValidationStatus ValidateUser( string userName , string emailAddr )
{
ValidationStatus status ;
string connectionString = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString ;
using ( SqlConnection con = new SqlConnection( connectionString ) )
using ( SqlCommand cmd = con.CreateCommand() )
{
cmd.CommandText + #"
select status = coalesce( ( select 1 from dbo.myTable t where t.UserName = #UserName ) , 0 )
+ coalesce( ( select 2 from dbo.myTable t where t.UserEmail = #UserEmail ) , 0 )
" ;
cmd.Parameters.AddWithValue( "#UserName" , userName ) ;
cmd.Parameters.AddWithValue( "#emailAddr" , emailAddr ) ;
int value = (int) cmd.ExecuteScalar() ;
status = (ValidationStatus) value ;
}
return status ;
}
Aside from anything else, hitting the DB twice for something like this is silly. And this more clearly expresses intent.
Then you can use it in your button click handler, something like this:
protected void Button_Click( object sender , EventArgs e )
{
string userName = Name_id.Text ;
string emailAddr = Email_id.Text ;
ValidationStatus status = ValidateUser( userName , emailAddr ) ;
switch ( status )
{
case ValidationStatus.Valid :
Label1.Text = "" ;
break ;
case ValidationStatus.EmailInUse :
Label1.Text = "Email address in use" ;
break ;
case ValidationStatus.UserNameInUse :
Label1.Text = "User name in use" ;
break ;
case ValidationStatus.EmailInUse|ValidationStatus.UserNameInUse:
Label1.Text = "Both user name and email address in use." ;
break ;
default :
throw new InvalidOperationException() ;
}
if ( status == ValidationStatus.Valid )
{
CreateNewUser() ;
}
}

Categories