I have a software which displays some tables from our SQL database. Now I want to add a tool, where I can change the password for my "testdummy" user.
I tried to open a connection again but it didn't help. If you need some additional code or informations, just write a comment.
Notice that I'm new to programming. I'm a apprentice and currently learning programming and administer databases. I know this is not the safest solution, but it's just a little task from my instructor. This software will not be released for customers.
Like I mentioned before, I tried to open a connection again before I want to change the password.
public void Change()
{
SqlConnection con = new SqlConnection();
string connectionString = GetConnectionString();
if (NewPassword.Password == NewPasswordAgain.Password && OldPassword.Password == GlobalData.Password)
{
try
{
//dbMan.TryConnect(connectionString);
//con.Open();
SqlConnection.ChangePassword($"Data Source={GlobalData.ServerName};Initial Catalog={GlobalData.DBName};UID={GlobalData.Username};PWD={OldPassword}", $"{NewPassword}");
}
catch (SqlException ex)
{
MessageBox.Show("Unable to change password. Try again!" + ex);
}
}
else
{
// If new Password doesn't match.
MessageBox.Show("Passwords doesn't match!");
}
}
I'm getting a SQL exception when I am trying to change the password.
(System.Data.SqlClient.SqlException (0x80131904): Login failed for user 'csharptest'.
I get this at:
SqlConnection.ChangePassword($"Data Source={GlobalData.ServerName};Initial Catalog={GlobalData.DBName};UID={GlobalData.Username};PWD={OldPassword}", $"{NewPassword}");
At this point of the programm, there should be a connection to the database, because I can handle some tables and manipulate the data sets.
But when I uncomment this:
//dbMan.TryConnect(connectionString);
//con.Open();
It goes into the catch brackets there:
public bool TryConnect(string connectionString)
{
conn = new SqlConnection();
conn.ConnectionString = connectionString;
try
{
conn.Open();
return true;
}
catch (Exception)
{
MessageBox.Show("Couldn't connect");
return false;
}
}
and returns following exception:
System.InvalidOperationException: 'Die ConnectionString-Eigenschaft wurde nicht initialisiert.'
In english it should be something like: "the connectionstring property has not been initialized"
Edit:
In the logs I'm getting this:
Login failed for user 'csharptest'. Reason: Password did not match that for the login provided.
Edit:
Instead of:
SqlConnection.ChangePassword($"Data Source={GlobalData.ServerName};Initial Catalog={GlobalData.DBName};UID={GlobalData.Username};PWD={OldPassword}", $"{NewPassword}");
I did this:
string updatePassword = "USE CSHARPTEST ALTER LOGIN [" + GlobalData.Username + "] WITH PASSWORD = '" + NewPassword + "'";
con.Open();
cmd.ExecuteNonQuery();
And now I think the only problem is the permission on the server.
You need to use parameters at the DbContext level. See this answer for more details, but, here's a code example (adapted from that same page):
string sql = "ALTER LOGIN #loginName WITH PASSWORD = #password";
ctx.Database.ExecuteSqlCommand(
sql,
new SqlParameter("loginName", loginName),
new SqlParameter("password", password));
The purpose of using the parameters here (and everywhere) is to prevent a SQL injection attack. This is especially important given that you are writing code that changes a password.
UPDATE
The ALTER LOGIN statement won't work with variables; it must be done through dynamic SQL. Here's an example of the updated code:
string sql = #"DECLARE #sql NVARCHAR(500)
SET #sql = 'ALTER LOGIN ' + QuoteName(#loginName) +
' WITH PASSWORD= ' + QuoteName(#password, '''')
EXEC #sql ";
ctx.Database.ExecuteSqlCommand(
sql,
new SqlParameter("loginName", loginName),
new SqlParameter("password", password));
Note we're still using the SqlParameters to prevent SQL injection attacks. We are also using the T-SQL method QuoteName to do proper quoting in the SQL we are generating; but this method simply doubles any [ characters (in the first call) or ' characters (in the second). There are many other vectors for a SQL injection attack, so merely relying on QuoteName wouldn't be enough.
Related
I have read the numerous posts on why you should give the using statement preference over manually doing .Open() then .Close() and finally .Dispose().
When I initially wrote my code, I had something like this:
private static void doIt(string strConnectionString, string strUsername)
{
SqlConnection conn = new SqlConnection(strConnectionString);
try
{
conn.Open();
string strSqlCommandText = $"CREATE USER {strUsername} for LOGIN {strUsername} WITH DEFAULT SCHEMA = [dbo];";
SqlCommand sqlCommand = new SqlCommand(strSqlCommandText, conn);
var sqlNonReader = sqlCommand.ExecuteNonQuery();
if (sqlNonReader == -1) Utility.Notify($"User Added: {strUsername}");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
finally
{
conn.Close();
conn.Dispose();
}
}
and this works... no problem. but only ONCE.
so, if I do something like this:
private static void doItLots(string strConnectionString, string strUsername)
{
for(int i=0; i<10; i++)
{
doIt(strConnectionString, $"{strUsername}_{i}");
}
}
it works the FIRST time when i=0, but any subsequent iterations fail with Cannot open database "myDbName" requested by the login. The login failed.
However, if I go back and comment out the conn.Dispose(); line, then it works fine on all iterations.
The problem is simply that if I want to do the .Dispose() part outside of the method, then I am forced to pass a SqlConnection object instead of simply passing the credentials, potentially making my code a bit less portable and then I need to keep the connection around longer as well. I was always under the impression that you want to open and close connections quickly but clearly I'm misunderstanding the way the .Dispose() command works.
As I stated at the outset, I also tried doing this with using like this...
private static void doIt(string strConnectionString, string strUsername)
{
using (SqlConnection conn = new SqlConnection(strConnectionString))
{
try
{
conn.Open();
string strSqlCommandText = $"CREATE USER {strUsername} for LOGIN {strUsername} WITH DEFAULT SCHEMA = [dbo];";
SqlCommand sqlCommand = new SqlCommand(strSqlCommandText, conn);
var sqlNonReader = sqlCommand.ExecuteNonQuery();
if (sqlNonReader == -1) Utility.Notify($"User Added: {strUsername}");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
finally
{
conn.Close();
}
}
}
and this does the exact same thing as the initial code with .Dispose() called manually.
Any help here would be greatly appreciated. I'd love to convert to the using statements but having trouble figuring out how to write reusable methods that way...
UPDATE:
I have narrowed it down a bit. The issue is NOT the iterations or making the calls over-and-over again. But I am still getting an access error. Here is the code:
string strConnectionString = $#"Data Source={StrSqlServerDataSource};Initial Catalog={StrDatabaseName};User id={StrSqlServerMasterUser};Password={StrSqlServerMasterPassword}";
using (SqlConnection connUserDb = new SqlConnection(strConnectionString))
{
try
{
Utility.Notify($"Connection State: {connUserDb.State.ToString()}"); // Responds as 'Closed'
connUserDb.Open(); // <-- throws error
Utility.Notify($"Connection State: {connUserDb.State.ToString()}");
Utility.Notify($"MSSQL Connection Open... Adding User '{strUsername}' to Database: '{strDatabaseName}'");
string sqlCommandText =
//$#"USE {StrDatabaseName}; " +
$#"CREATE USER [{strUsername}] FOR LOGIN [{strUsername}] WITH DEFAULT_SCHEMA = [dbo]; " +
$#"ALTER ROLE [db_datareader] ADD MEMBER [{strUsername}]; " +
$#"ALTER ROLE [db_datawriter] ADD MEMBER [{strUsername}]; " +
$#"ALTER ROLE [db_ddladmin] ADD MEMBER [{strUsername}]; ";
using (SqlCommand sqlCommand = new SqlCommand(sqlCommandText, connUserDb))
{
var sqlNonReader = sqlCommand.ExecuteNonQuery();
if (sqlNonReader == -1) Utility.Notify($"User Added: {strUsername} ({sqlNonReader})");
}
result = true;
}
catch (Exception ex)
{
Utility.Notify($"Creating User and Updating Roles Failed: {ex.Message}", Priority.High);
}
finally
{
connUserDb.Close();
Utility.Notify($"MSSQL Connection Closed");
}
}
return result;
}
The error I am getting here is: Cannot open database requested by the login. The login failed.
One clue I have is that prior to this, I was running this same code with two changes:
1) uncommented the USE statement in the sqlCommandText
2) connected to the Master database instead
When I did that, it didn't work either, and instead I got this error: The server principal is not able to access the database under the current security context.
If I go into SSMS and review the MasterUser they are listed as db_owner and I can perform any activities I want, including running the command included in the code above.
I rewrote all the code to make use of a single connection per the recommendations here. After running into the "server principal" error, I added one more connection to attempt to directly connect to this database rather than the master.
UPDATE 2:
Here is another plot twist...
This is working from my local computer fine (now). But, not (always) working when run from an Azure Webjob that targets an Amazon Web Services (AWS) Relational Database Server (RDS) running MSSQL.
I will have to audit the git commits tomorrow, but as of 5p today, it was working on BOTH local and Azure. After the last update, I was able to test local and get it to work, but when run on Azure Webjob it failed as outlined above.
SqlConnection implements IDisposable. You don't call dispose or close.
try{
using (SqlConnection conn = new SqlConnection(strConnectionString))
{
conn.Open();
string strSqlCommandText = $"CREATE USER {strUsername} for LOGIN {strUsername} WITH DEFAULT SCHEMA = [dbo];";
SqlCommand sqlCommand = new SqlCommand(strSqlCommandText, conn);
var sqlNonReader = sqlCommand.ExecuteNonQuery();
if (sqlNonReader == -1) Utility.Notify($"User Added: {strUsername}");
}}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
How to restore SQL Server backup using C#?
try
{
string test = "D:\\backupdb\\05012017_130700.Bak";
sqlcmd = new SqlCommand("Restore database EmpolyeeTable from disk='D:\\backupdb\\05012017_130700.Bak'", con);
sqlcmd.ExecuteNonQuery();
Response.Write("restore database successfully");
}
catch (Exception ex)
{
Response.Write("Error During backup database!");
}
Quite weird requerement you have right there. I´ve never heard of someone restoring a database backup from a webpage, and as #Alex K. told, it would be quite rare that the user that uses your web application have the required previleges.
Anyway, supposing that everything told above is OK, the code to restore a backup would be this:
Use this:
using System.Data;
using System.Data.SqlClient;
Code:
private void TakeBackup()
{
var conn = new SqlConnection("Data Source=" + Server + ";Initial Catalog=" + Database + ";User Id=" + Username + ";Password=" + Password + ";");
try
{
conn.Open();
SqlCommand command = conn.CreateCommand();
command.CommandText = "RESTORE DATABASE AdventureWorks FROM DISK = 'C:\AdventureWorks.BAK' WITH REPLACE GO";
command.ExecuteNonQuery();
}
catch (Exception)
{
throw;
}
finally
{
conn.Dispose();
conn.Close();
}
}
This is going to work specifically for the problem you posted. Be sure to set all the parameters of your database server on the connection string, it seems from the comments on your question that you are having communication issues. You have to solve that problems before you do anything. Some tips for that:
Be sure you set all the parameters on connection string the right way
Try to connect using another tool like ODBC so you can test all parameters
Check out SQL Network settings to see if TCP/IP is enabled
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.
I'm just a beginner in C#. I'm using XAMPP server for MySQL database and Visual C# 2010. Then I have created a database named "testdb" in phpMyAdmin and a table named "login". I have inserted my username and password in the table. I'm doing a simple WinForm login where I made two text boxes for username and password and a button. I have my codes done and there's no compiler error. But I had troubled in one line. It says "Unable to connect to any of the specified MySQL hosts". I added MySql.Data to my references. I want to fetch the data in the database table when I'm going to log in. Then authorize the user or if not matched, it will prompt an error message.
Here is my code:
using MySql.Data.MySqlClient;
public bool Login(string username, string password)
{
MySqlConnection con = new MySqlConnection("host=localhost;username…");
MySqlCommand cmd = new MySqlCommand("SELECT * FROM login WHERE username='" +
username + "' AND password='" + password + "';");
cmd.Connection = con;
con.Open(); // This is the line producing the error.
MySqlDataReader reader = cmd.ExecuteReader();
if (reader.Read() != false)
{
if (reader.IsDBNull(0) == true)
{
cmd.Connection.Close();
reader.Dispose();
cmd.Dispose();
return false;
}
else
{
cmd.Connection.Close();
reader.Dispose();
cmd.Dispose();
return true;
}
}
else
{
return false;
}
}
*I hope for your your feedback. :)
Your immediate problem is probably either an incorrect connection string or the database server is not available. The connection string should be something like this
Server=localhost;Database=testdb;Uid=<username>;Pwd=<password>;
with <username> and <password> replaced with your actual values.
Besides that your code has several issues and you should definitely look into them if this is intended to become production code and probably even if this is just a toy project to learn something. The list is in particular order and may not be comprehensive.
Do not hard code your connection string. Instead move it to a configuration file.
Do not include plain text passwords in configuration files or source code. There are various solutions like windows authentication, certificates or passwords protected by the Windows Data Protection API.
Do not just dispose IDisposable instances by calling IDisposable.Dispose(). Instead use the using statement to release resources even in the case of exceptions.
Do not build SQL statements using string manipulation techniques. Instead use SqlParameter to prevent SQL injection attacks.
Do not store plain text passwords in a database. Instead at least store salted hashes of the passwords and use a slow hash function, not MD5 or a member of the SHA family.
You can use IDbCommand.ExecuteScalar to retrieve a scalar result and avoid using a data reader.
Comparing a boolean value with true or false is redundant and just adds noise to your code. Instead of if (reader.IsDBNull(0) == true) you can just use if (reader.IsDBNull(0)). The same holds for if (reader.Read() != false) what is equivalent to if (reader.Read() == true) and therefore also if (reader.Read()).
Using an O/R mapper like the Entity Framework is usually preferred over interacting with the database on the level of SQL commands.
Try modifying your ConnectionString accordingly to the Standard MySQL ConnectionString:
Server=myServerAddress;Database=myDataBase;Uid=myUsername;Pwd=myPassword;
Source:
MySQL ConnectionStrings
You can also take a look at the following link, that shows how to connect to a MySQL database using C#:
Creating a Connector/Net Connection String (MYSQL)
Make it simple and sql injection free, and also don't forget to add MySql.Web
in your references since your using XAMPP
public bool Login(string username, string password)
{
DataTable dt = new DataTable();
string config = "server=....";
using (var con = new MySqlConnection { ConnectionString = config })
{
using (var command = new MySqlCommand { Connection = con })
{
con.Open();
command.CommandText = #"SELECT * FROM login WHERE username=#username AND password=#password";
command.Parameters.AddWithValue("#username", username);
command.Parameters.AddWithValue("#password", password);
dt.Load(command.ExecuteReader());
if (dt.Rows.Count > 0)
return true;
else
return false;
} // Close and Dispose command
} // Close and Dispose connection
}
I am using this code to delete a database through C#
Int32 result = 0;
try
{
String Connectionstring = CCMMUtility.CreateConnectionString(false, txt_DbDataSource.Text, "master", "sa", "happytimes", 1000);
SqlConnection con = new SqlConnection();
con.ConnectionString = Connectionstring;
String sqlCommandText = "DROP DATABASE [" + DbName + "]";
if (con.State == ConnectionState.Closed)
{
con.Open();
SqlConnection.ClearPool(con);
con.ChangeDatabase("master");
SqlCommand sqlCommand = new SqlCommand(sqlCommandText, con);
sqlCommand.ExecuteNonQuery();
}
else
{
con.ChangeDatabase("master");
SqlCommand sqlCommand = new SqlCommand(sqlCommandText, con);
sqlCommand.ExecuteNonQuery();
}
con.Close();
con.Dispose();
result = 1;
}
catch (Exception ex)
{
result = 0;
}
return result;
But I get an error
Database currently in use
Can anyone help?
Try this:
String sqlCommandText = #"
ALTER DATABASE " + DbName + #" SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
DROP DATABASE [" + DbName + "]";
Also make sure that your connection string defaults you to the master database, or any other database other than the one you're dropping!
As an aside, you really don't need all of that stuff around your queries. The ConnectionState will always start off Closed, so you don't need to check for that. Likewise, wrapping your connection in a using block eliminates the need to explicitly close or dispose the connection. All you really need to do is:
String Connectionstring = CCMMUtility.CreateConnectionString(false, txt_DbDataSource.Text, "master", "sa", "happytimes", 1000);
using(SqlConnection con = new SqlConnection(Connectionstring)) {
con.Open();
String sqlCommandText = #"
ALTER DATABASE " + DbName + #" SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
DROP DATABASE [" + DbName + "]";
SqlCommand sqlCommand = new SqlCommand(sqlCommandText, con);
sqlCommand.ExecuteNonQuery();
}
result = 1;
Here is how you do it using Entity Framework version 6
System.Data.Entity.Database.Delete(connectionString);
You should take a look at SMO.
These allow you to manage all aspects of SQL Server from code, including deleting of databases.
The database object has a Drop method to delete database.
Create sqlconnection object for different database other than you want to delete.
sqlCommandText = "DROP DATABASE [DBNAME]";
sqlCommand = new SqlCommand(sqlCommandText , sqlconnection);
sqlCommand.ExecuteNonQuery();
In this case i would recommend that you take the database offline first... that will close all connections and etc... heres an article on how to do it: http://blog.sqlauthority.com/2010/04/24/sql-server-t-sql-script-to-take-database-offline-take-database-online/
Microsoft clearly states that A database can be dropped regardless of its state: offline, read-only, suspect, and so on. on this MSDN article (DROP DATABASE (Transact-SQL))
Connection pooling at a guess, use sql server's activity monitor to make sure though.
Pooling keeps connections to the database alive in a cache, then when you create a new one, if there's one in the cache it hands it back instead of instantiating a new one. They hang around for a default time, (2 minutes I think) if they don't get re-used in that time, then they killed off.
So as a first go connect straight to master, instead of using change database, as I suspect change database will simply swap connections in the pool.
Add a check routine for database in use (use a connection to master to do it!). You can force the database to be dropped anyway by first executing
ALTER DATABASE [MyDatabase] SET SINGLE_USER WITH ROLLBACK IMMEDIATE
again from the connection to master!
However everybody else using the db, will no longer like you at all...
Just don't use DB name in connection string.
"Data Source=.\SQLEXPRESS;Integrated Security=True;"
I was having the same troubles as Anshuman...
By my testing of the code in question of Anshuman there have been very simple error:
there have to be SqlConnection.ClearAllPools(); instead of SqlConnection.ClearPool(con);
Like this trouble of
"cannot drop database because is in use..."
disappears.