SQL query getting Server name in triggers when using ASP.NET - c#

I have a trigger set on SQL table for Audit Trail purpose.
Here is the trigger code.
ALTER TRIGGER [dbo].[tri_bowzer_UPDATE] ON [dbo].[Bowzer]
For Update
AS
INSERT Table_Audit(TableName, Action, UserName, ComputerName)
SELECT
'bowzer', 'U', suser_sname(), host_name()
It works fine and shows the username and the computer (client) name in Desktop applications. but in ASP.NET applications, I use a common SQL login so that database operations can be performed using this login. I understand that SQL Server is getting server machine name because of this ID. However, I want to capture the client machine name whenever a database operation is performed.
What changes can be made to get client machine name??

You need to tweak your connection string to specify the connectionstring property WSID (Workstation id) as below
string strconn = "data source=SQLSERVER;initial catalog = DBNAME ; uid=sa;pwd=password; WSID=" + (System.Net.Dns.GetHostEntry(Request.ServerVariables["remote_addr"]).HostName);
and then you will get HOST_NAME() value as client machine name in your trigger
MSDN : http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnection.connectionstring.aspx

You can set the client's IP address using T-SQL SET CONTEXT_INFO, and retrieve this data in the trigger using the CONTEXT_INFO function.

This design is not really suitable for what you are trying to achieve. I'd consider adding updated by column in your database where you can store user id or machine name of a user who updated data.
This way you'll keep the trigger logic simple and avoid too many connections to your database that might degrade performance.

Related

Getting local computer name or IP address from inside of a SQL CLR in RDP Session

I have a C# (.NET 4.5) SQL CLR (SQL Server 2012, Windows Server 2008 R2). Users login via RDP to run an app which in turn calls this CLR.
I am trying to find out either the local computer name or the local IP address from inside of the CLR.
I have dumped the whole environment to a log file from the CLR and there is no CLIENTNAME variable set (not surprising, since it is likely the SQL process' environment rather than the user's), so I cannot use that.
I tried importing the Cassia DLL and using it, but the ClientIPAddress of the TerminalServicesManager class is null, so that does not seem to work either.
Is there any way to get the local computer name or IP from a SQL CLR?
It sounds like you are using "local" to mean the client's (i.e. the end-user; the one using Remote Desktop to get to the server) IP Address. This is definitely an interesting (and tricky) problem to solve given that the connection to SQL Server is being made from the local machine due to using RDP. And if you shell out of SQL Server's process, either via xp_cmdshell or SQLCLR, you are now in a subprocess that originated from a process on the server, not on the client's machine. There is pretty much no connection between the client's machine and SQL Server.
Fortunately, there is a way to get this info IF each user is connecting with their own Login (Windows Login or SQL Server Login) instead of a shared one (a shared Login is more likely to happen when using SQL Server Logins).
If everyone has their own individual Login, then you can do the following:
Create a table to hold fields such as:
[Login] NVARCHAR(50) NOT NULL
ClientName NVARCHAR(50) NOT NULL
CreateDate DATETIME NOT NULL
CONSTRAINT [DF_TableName_CreateDate] DEFAULT (GETDATE())
Create a Logon Script (a .cmd script) that will be associated with their profile, at least on that server, if not in their Active Directory Roaming Profile (if you are using AD).
The Logon Script will simply call SQLCMD to insert the environment variable CLIENTNAME into the table:
SQLCMD -Q "INSERT INTO dbo.ClientHostNames (fields) VALUES (ORIGINAL_LOGIN(), N'%CLIENTNAME%');"
This works because DOS variables are substituted before the command is executed.
In your SQLCLR code, make a connection using the in-process "Context Connection = true;" ConnectionString, since this will execute as the Login running the SQLCLR code.
Get the hostname using a query similar to:
SELECT HostName
FROM dbo.ClientHostNames
WHERE WindowsLogin = ORIGINAL_LOGIN()
ORDER BY CreateDate DESC;
You can just use SqlCommand.ExecuteScalar() to get the HostName value.
It's not the most direct means, but so far I can't find anything else that will work. And I have tried setting a User environment variable and then trying to grab that in SQLCLR while using Impersonation, but that doesn't seem to work.
Try it .. Hopefully it will work
public string GetIPAddress()
{
System.Net.IPHostEntry ipHostInfo = System.Net.Dns.GetHostEntry(System.Net.Dns.GetHostName()); // `Dns.Resolve()` method is deprecated.
string tempIp = string.Empty;
foreach (var item in ipHostInfo.AddressList)
{
tempIp = item.ToString();
//Make your comparison here
}
return tempIp;
}

Pass addition information in Connection String

I want to pass an additonal string value in connection string so that can see which person was connected.
This connection string is login as sql authentication and log file will show sql user id and computer name but i want to add user name who is connected to my applicaiton.
Is there a way to pass user information who is using that application and add in sql server log file. e.g. message.
string constr = "WorkStation id=computerName;"
+ "packet size=0000"
+ ";user id=user001;"
+ "data source=servername;"
+ "persist security info=True;"
+ "initial catalog=database"
+ ";password=password";
A connection string is intended as a single string that is used by all the users. It's also something that usually is shared by multiple users, because a database has a limited amount of open connections possible. If you have a connection open for every user, you're going to run into scaling issues and connection pool overflows.
If you want to know what user issued the command, the best solution is to use a stored procedure that takes the user information as a parameter and logs it through that.
However, passing user info through the logic layer to the data layer is a code smell. If you need to know who did a transaction, it's better that you log this in the application code instead of through the SQL database.
There is simply no way of doing that directly in to the SQL Logs.
If you want to log the usage you have 2 good options:
1) Create a different SQL user for each username in your application. This is probably only possible if you have Active Directory integration, otherwise it could be a very tedious proces of adding new SQL users
2) Log your application usage from the application into a seperate SQL table. This is how a lot of applications works, and would be considered best practice in this case.

Save win user name by SQL server trigger

There is a local FOX app connected to SQL Server database. There is a trigger in the database which saves changes to the db table like:
INSERT INTO dbo.UpdateContract
SELECT 'OLD' RecordType,USER,SYSTEM_USER,#Time, (...)
I am developing the same app by .NET (C#) and don't know how to use the same trigger to save the name of user who is logged into my .NET app (or windows user).
I can use static cs like
SqlConnection sqlConn = new SqlConnection("Server=servername;Database=dbname;User Id=dbuser;Password=Pass;");
(but, sure, the trigger save this user name and not app user name )
Or this cs:
SqlConnection sqlConn = new SqlConnection("Data Source=servername; Initial Catalog=dbname; Integrated Security=SSPI;");
(but in this case the trigger uses the server machine name)
You can use the SYSTEM_USER function with DEFAULT constraints in the CREATE TABLE and ALTER TABLE statements. You can also use it as any standard function.
If the user name and login name are different, SYSTEM_USER returns the login name.
If the current user is logged in to SQL Server by using Windows Authentication, SYSTEM_USER returns the Windows login identification name in the form: DOMAIN\user_login_name. However, if the current user is logged in to SQL Server by using SQL Server Authentication, SYSTEM_USER returns the SQL Server login identification name, such as WillisJo for a user logged in as WillisJo.
SYSTEM_USER returns the name of the currently executing context. If the EXECUTE AS statement has been used to switch context, SYSTEM_USER returns the name of the impersonated context.
There's a chance that your "non-C# version" is impersonating the context. You can apply this connection after you've connected to server:
EXECUTE AS USER = 'YourWindowsUsername';
that should switch the context. I am not sure if that's the correct approach, you might want to add new field to trigger: CURRENT_USER, this might be something you need.
Source: http://msdn.microsoft.com/en-us/library/ms176050(v=sql.105).aspx

Access denied for user 'root'#'localhost' (using password: YES) in Visual Studio

Kindly bear with me. I am a Microsoft SQL Server person with loads of Visual Studio experience, but I need to get something done using a MySQL database.
I am trying to create a little tool here that will allow our developers to quickly update database records, and I am using Visual Studio to create a small Windows Form to do this.
In a Microsoft SQL Server connection string, I could write something like this:
Server=myServerAddress;Database=myDataBase;User Id=username;Password=password;
In a MySQL connection string, there appear to be multiple other options, but the first one looks basically the same:
Server=myServerAddress;Database=myDataBase;Uid=username;Pwd=password;
When I attempt to open the MySQL connection from my PC, I get the exception listed in the title (actually, it shows the Uid value and the IP Address of my PC instead of localhost, but I am hoping more people will recognize the error easier this way):
public static void MySQLi_Connect() {
m_err = null;
var str = Properties.Settings.Default.ConnStr;
try {
m_conn = new MySqlConnection(Properties.Settings.Default.ConnStr);
m_conn.Open();
} catch (MySqlException err) {
ErrorLog("MySQLi_Connect", err);
}
}
I did a search, and it seems that the Uid on MySQL needs to be granted access from the specific IP Address that the connection is being made from.
Further, I found this on the mysql.com doc pages:
If you do not know the IP address or host name of the machine from which you are connecting, you should put a row with '%' as the Host column value in the user table. After trying to connect from the client machine, use a SELECT USER() query to see how you really did connect. Then change the '%' in the user table row to the actual host name that shows up in the log. Otherwise, your system is left insecure because it permits connections from any host for the given user name.
A few things:
It looks like I can connect to MySQL by using a % setting in the Uid jp2code, but MySQL says I need to change that back right away to remove system vulnerability.
Microsoft SQL Server did not seem to require this - or, if it did, I simply never was slapped in the face with this vulnerability issue like MySQL is doing.
Now, I ask:
If this is going to be a tool used by different developers on different PCs, is it common practice to turn the blind eye to this horrendous system vulnerability?
Is this not really as big of a concern as MySQL is making it appear?
What is the best way to continue with a Windows Forms application that needs to connect from various locations? Obviously, I do not want to continuously be adding more entries for a particular application every time another developer wants to use the tool or someone tries to run it from a different PC.
You can configure the security of your MySQL server as strong as you like, usually you dont connect users but applications. So if you have your root user without password in production environment is your fault. Usually developers have access to development environment, so this is not a big deal.
Of course try to have as many users as roles you need, for your example I think one user is enough. In production use a secure config file for save a secure password and set you mysqlserver restricted.
I was having the same issue and I found out that the password wasn't correct.
GO to your sql command line and type the code below:
mydb in the line below is the name of the database you are working on.
passwd in the line has to match the password you have in c# code so in your case "password"
grant all privileges on mydb.* to myuser#localhost identified by 'passwd';
Like OP says you can wildcard the hostname portion. I used this on our dev-server (not recommended for production servers):
update mysql.user set host = '%' where host='localhost';
Then I had to restart the server to make MySQL use it (propably I could just have restarted the MySQL service).

INSERT statement doesn't work in WEB SERVER

IN localhost insert statement WORKS PERFECTLY ( insert data in database sql management server) but from web server it doesn't ( update, delete works but not insert).
I am using sql connection , string str = insert into dtbase.dbo.candidat values ().
command cmd = new command (sql, connection)
Can someone please tell me why it doesn;t work from wb server ( I am using web application.) do i need to add some permision in web.config?
To determine if this is a permissions issue (which I think it is) or not then temporarily (this is for the down voters out there) enable ASP.Net Impersonation by using an account that you know has access to your network and SQL Server instance: http://support.microsoft.com/kb/306158
Based on the other comments, I agree that it sounds like a permissions issue.
You may be getting the error using database.dbo.table because your table was created under a different schema (ie. database.user.table) and you're trying to access that schema from a user that doesn't have permissions to that schema.
Does your connection string change from localhost to your production server?

Categories