Check if username/password exists in multiple tables - c#

I am working on a login page. I'd like to check if the username & password exists in the database. I have three database tables : Teams,Organizers,Admins with username & password field in each table respectively. I am implementing the login in three-tier architecture.
I believe that I have a problem with my SQL statement below. I tested my sql query with a distinct/valid team username and team password. The COUNT query returns more than one row, which is incorrect.
This are my codes for the data access layer :
public int getExistingAccount(string username, string password)
{
string queryStr = "SELECT COUNT(*) FROM Teams t,Organizers o,Admins a WHERE (t.teamUsername=#username AND t.teamPassword=#password) OR (o.organizerUsername=#username AND o.organizerPassword=#password) OR (a.adminUsername=#username AND a.adminPassword=#password)";
SqlConnection conn = new SqlConnection(_connStr);
SqlCommand cmd = new SqlCommand(queryStr, conn);
cmd.Parameters.AddWithValue("#username", username);
cmd.Parameters.AddWithValue("#password", password);
int returnValue = 0;
conn.Open();
returnValue = (int)cmd.ExecuteScalar();
conn.Close();
return returnValue;
}
As for the business logic layer codes :
public string getAccount(string username, string password)
{
string returnMessage = "";
if (username.Length == 0)
returnMessage += "Username cannot empty</br>";
if (password.Length == 0)
returnMessage += "Password cannot be empty</br>";
if (username.Equals(password))
{
returnMessage += "Duplicate value. Please try again</br>";
}
//Invoke validateInput() method to validate data
if (returnMessage.Length == 0)
{
int noOfRows = 0;
LogAccounts logInd = new LogAccounts();
noOfRows = logInd.getExistingAccount(username, password);
if (noOfRows > 0)
returnMessage += "Account found";
else
returnMessage += "Invalid username/password.";
}
return returnMessage;
}

Try this, select from each table and UNION ALL the results, then count the rows.
select count(*) from
(
SELECT 1 as dummyname FROM Teams t
WHERE (t.teamUsername=#username AND t.teamPassword=#password)
union all
SELECT 1 FROM Organizers o
WHERE (o.organizerUsername=#username AND o.organizerPassword=#password)
UNION ALL
select 1 from Admnis
WHERE (a.adminUsername=#username AND a.adminPassword=#password)
)

I seems you have a really awkward database design, where fetching a single user requires a unnaturally large/long sql query.
In almost every use case you would have a single Users table, and if you need to tie the user to some additional information, you would have a reference to the user table by the UserId. You should read up on foreign keys aswell.
Quick sample:
Users:
- UserId (int or guid) (primary key)
- .... (additional fields removed for brewity)
The other tables would refer to the UserId column, and use that to pull information about the user with a join.
E.g.: SELECT T.*, U.* FROM Teams T INNER JOIN Users U ON U.UserId = T.UserId WHERE U.Username = "AwesomeCoach";
A simple validate query would be something like this:
SELECT COUNT(*) FROM Users WHERE Username = xx AND Password = xx
That would return an integer that specifies how many rows that matched the given username/password combination. It should be either 1 or 0. Put a Unique contraint on the Username column to ensure that there are only one occurence of each Username.
Footnote: I see that you have got an answer that solves the problem you were facing, but I would recommend that you read up on some database design, and try to keep it as simple as possible. Managing multiple users across multiple tables can and will be a hassle as the application grows.

Your design is really bad, you should have all users in one table. After that if you want to take user by id, you should check 3 diff tables. Anyway the problem is in the query you should write it like this:
string queryStr = #"
SELECT
COUNT(*) AS TeamsCount,
(SELECT COUNT(*) Organizers WHERE organizerUsername=#username AND organizerPassword=#password) AS OrgCount,
(SELECT Count(*) Admins WHERE adminUsername=#username AND adminPassword=#password) AS AdminCount
FROM
Teams
WHERE
teamUsername=#username AND
teamPassword=#password";
The query should look something like this. After that you need to return this in DataSet. You need:
DataSet dst = new DataSet();
using(SqlAdapter adapter = new SqlAdapter(cmd))
{
adapter.Fill(dst);
}
In this case you will have dst with 3 columns in it. Check for existing user should be:
if(dst.Tables[0].Rows[0]["TeamsCount"] > 0 ||
dst.Tables[0].Rows[0]["OrgCount"] > 0 ||
dst.Tables[0].Rows[0]["AdminCount"] > 0)
{
//user already exist !
}

Related

Verify if username exists when updating username

In this situation, there is a form used for updating user information (username, password, mobile number, etc.).
Below is the code I used to check if the username exists in the database:
string sql = "SELECT username FROM (SELECT username FROM useraccount WHERE username != #old_uname)ua WHERE BINARY username = #new_uname LIMIT 1;";
MySqlCommand cmd = new MySqlCommand(sql, SecurityMod.dbconn());
cmd.Parameters.AddWithValue("#old_uname", old_uname);
cmd.Parameters.AddWithValue("#new_uname", new_uname);
if (cmd.ExecuteScalar() == null)
{
isValidNewUname = true;
}
It works if the user really changes his/her username. But the problem occurs when the user made changes to anything but the username field. The isValidNewUname variable remains false. Any ideas and suggestions would be a big help.
Instead of basing on fetched username, I opted instead for the uid. I solved the problem with the following code:
string sql = "SELECT uid FROM useraccount WHERE BINARY username = #new_uname;";
MySqlCommand cmd = new MySqlCommand(sql, SecurityMod.dbconn());
cmd.Parameters.AddWithValue("#new_uname", new_uname);
if (cmd.ExecuteScalar() == null || cmd.ExecuteScalar().ToString() == userID)
{
isValidNewUname = true;
}
This code is shorter and more simple hence easier to maintain. What happens here is that the query looks for the uid of the #new_uname in the database. It then compares the fetched uid to the uid of the current user. If it is the same, it means that the fetched result of the query is simply the user itself, so it doesn't really matter if their the same or not since it belongs to only one user. If the fetched uid is not equal to the uid of the current user, it means the username input by the user is already taken so isValidNewUname would be false. If the query above doesn't return results, then the #new_uname is available since it doesn't have a record in the database.

Get data from SQL table to empty variable

I'm trying to search in table(called Accounts) for Username and in the line of this user in coulmn 4 that called PhotoId there is string that i need to get and add it to empty string value that i made before. thats what i tryied
i want to do it by Query like my code
string ecid = e.CallbackQuery.Message.Chat.Id.ToString();
using (SqlCommand sqlCommand = new SqlCommand("SELECT * from Accounts where Username like #username", con))
{
con.Open();
sqlCommand.Parameters.AddWithValue("#username", ecid);
string variable;
string userfound = (string)sqlCommand.ExecuteScalar();
con.Close();
if (userfound == ecid)
{
using (SqlCommand GotPhoto = new SqlCommand("SELECT PhotoId from Accounts where Username like #user", con))
{
con.Open();
GotPhoto.Parameters.AddWithValue("#user", ecid);
DataPid = GotPhoto.ExecuteScalarAsync().ToString();
con.Close();
// await bot.SendPhotoAsync("xxx", DataPid, capo, parseMode: ParseMode.Markdown, replyMarkup: zz);
await bot.SendTextMessageAsync(cid, "its been Poster", parseMode: ParseMode.Html);
}
}
else
{
MessageBox.Show("Not Working");
}
}
You are combining a SELECT * ... with ExecuteScalar(), which "returns the value of the first column in the first row of the resultset. Additional rows and columns are ignored."(https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.executescalar(v=vs.110).aspx). Probably username is not the first column in the accounts table, so the content of another column is retured and your comparison fails.
Use SELECT username from Accounts where Username like #username instead.
EDIT
Depending on your data-model you can also combine the two queries into one as you are querying the same row twice.
con.Open();
using (var cmd = new SqlCommand("SELECT PhotoId from Accounts where Username like #user", con)) {
cmd.Parameters.AddWithValue("#user", ecid);
using (var reader = await cmd.ExecuteReaderAsync()) {
if (!reader.HasRows || ! reader.Read()) {
MessageBox.Show("User not found");
} else {
DataPid = cmd.GetYOURDATATYPE(0).ToString();
//....
}
}
}
con.Close();
According to the documentation of ExecuteScalar =>
Executes the query, and returns the first column of the first row in the result set returned by the query. Additional columns or rows are ignored.
When you perform string userfound = (string)sqlCommand.ExecuteScalar();
You are comparing the username to a value ( but wich one ? the value of the first column in the create table instruction)
I Suggest you change the first query to be sure to select username
PS:
To be consistant, you can also rename both of your query parameter #username instead of #user and #username because it refers exactly to the same thing.
You can perform only one query instead of two. ( select photoid ... where username = .. , and just check if the result contains one row )

C# returning row value (rank) after Order By via SQLite

My goal is to write a code order my table by "Currency". It will then using the string input of "User" and find that user after the Order By query. It will return the row number of that user which is basically the rank. Is this the most efficient way to do it? I found out I have to actually count each row which doesn't seem efficient. Thank you all in advance.
public string rank(string user)
{
String sql = "SELECT user FROM '" + channel + "' ORDER BY currency DESC LIMIT 10";
String rank = "";
using (cmd = new SQLiteCommand(sql, myDB))
{
using (SQLiteDataReader r = cmd.ExecuteReader())
{
///* Calculate the row number after order by *////
}
}
return rank;
}
The rank of the user is equivalent to the number of users who have a currency value greater than the user's own currency value.
As per this answer, https://stackoverflow.com/a/5685623/3828960, you can count the number of users using a subquery:
SELECT
chan1.*,
(
SELECT COUNT(*)
FROM channel AS chan2
WHERE chan2.currency > chan1.currency
) as ChanRank
FROM channel as chan1
WHERE chan1.user = 'UserName'

How to use reader.GetDecimal() for a column in a separate table?

I have two tables in my database: Students, and accounts. In my application, I am trying read data from multiple tables using reader.GetDecimal() method and having trouble getting data from the second table.
Is it possible to do this using the GetDecimal method? Or do I need to add to one of the queries to get what I need from the Accounts table?
Database Tables:
Accounts
Student
Code:
//Query Student table for bAlertSetup
SqlCeCommand AlertQuery = new SqlCeCommand("SELECT * from Students AND Accounts", conn);
reader = AlertQuery.ExecuteReader();
while (reader.Read())
{
bSinglePersonAlertSetup = reader.GetBoolean(5);
if (bSinglePersonAlertSetup == true)
{
int AccountID = reader.GetInt32(7);
decimal Threshold = reader.GetDecimal(6);
//decimal Total = get decimal from the accounts table where accountID (in the accounts table) = AlertAccountID
//See if Students account is below the defined threshold
if (Total < Threshold)
{
StudentEmailAddress = reader.GetString(3);
if (StudentEmailAddress != null)
{
Console.WriteLine(StudentEmailAddress);
mail.To.Add(StudentEmailAddress);
//Update bAlertSetup
SqlCeCommand UpdateBool = new SqlCeCommand("UPDATE Students set bSendAlert = 0 WHERE UserId = #ID");
UpdateBool.Parameters.AddWithValue("#ID", reader.GetInt32(0));
UpdateBool.Connection = conn;
UpdateBool.ExecuteNonQuery();
}
}
}
EDIT:
Here is the new query I am using that I believe joins the two tables correctly with the columns needed. Now, how do I get the index of each column to use in the GetDecimal() Method?
SqlCeCommand AlertQuery = new SqlCeCommand("SELECT st.bAlertSetup, st.AccountThreshold, st.AlertAccountID, acc.AccountID, acc.AccountTotal FROM Students st INNER JOIN Accounts acc ON st.AlertAccountID = acc.AccountID", conn);
SqlCeCommand AlertQuery = new SqlCeCommand("SELECT S.*,A.AccountTotal from Students S Inner Join Accounts A ON S.AlertAccountID = A.AccountID");
Make sure to execute the query in sql and know the index of each column to provide the correct index, or replace S.* by S.[FieldName] for each field comma seperated and that the order of your columns will be always preserved in the query.
You should properly join the two tables in your query, and only select the individual columns you need. So your query might be something like below...
SELECT st.UserID, st.FirstName, st.LastName, acc.AccountName, acc.AccountTotal
FROM Student st
JOIN Accounts acc
ON st.AlertAccountID = acc.AccountID
You could get the AccountTotal then by name, like this...
decimal total = reader.GetDecimal("AccountTotal");
http://msdn.microsoft.com/en-us/library/system.data.datatablereader.getdecimal(v=vs.90).aspx
You can use this
decimal total = reader.GetDecimal(2); //index column name
hope will help you

Iterating through a column in a database

When 'login' button is clicked I would like to iterate through a column in a table and check if a match occurs. How would I go about doing this?
I have connected through to a database and I'm reading from database and writing to database fine. I am not sure how I would iterate through a database.
P.S I'm new to both c# and visual studios. I am not having much trouble with C#, since I come over from Java however I'm struggling to get into grips with Visual studios.
This is simple you'll see.
SqlConnection myConnection = new SqlConnection(#"Server = (Local); Integrated Security = True;" + "Database = insertDataBaseName"); // Assuming (Local)
myConnection.Open();
SqlCommand myCommand = myConnection.CreateCommand();
myCommand.CommandText = ("SELECT UserName, Password,from Login"); // Where Login is your table . UserName and Password Columns
SqlDataReader myReader = myCommand.ExecuteReader();
bool login = false;
while (myReader.Read())
{
if (userNameBox.Text.CompareTo(myReader["UserName"].ToString()) == 0 && passwordBox.Text.CompareTo(myReader["Password"].ToString()) == 0) // A little messy but does the job to compare your infos assuming your using a textbox for username and password
{
login = true;
}
}
if (login)
{
//Your're in.
}
else
{
MessageBox.Show("Invalid UserName or Password", "Access Denied"); // Error message
}
myReader.Close();
myConnection.Close(); // Just close everything
Hope this helps.
Dont hesitate if you have any question on this code part.
in sql something like this will help
Select top(1) from Users where Id = #Id
or in linq
var user = (from u in users
where u.Id == id
select u).SingleOrDefault();
If you are chekcing for a username password validation, I think you should not get all user records and loop Iterate thru that. What if you get 100000 user registrations ? You really want to iterate 100000 times ? Really ?
You should probably query for the purticular record you are looking for
Some thing like this
SELECT TOP 1 UserID,FIRSTNAME,LASTNAME,HASHED_PASSWORD,SALT WHERE USERNAME='kristy'
Execute that query againinst your database and see whether you have any records exist, If you have one record present, now you can validate the password with the data you have.

Categories